]> dgit.raspbian.org Git - pyside6.git/commitdiff
New upstream version 6.8.2.1
authorStuart Prescott <stuart@debian.org>
Sat, 15 Feb 2025 13:28:09 +0000 (00:28 +1100)
committerStuart Prescott <stuart@debian.org>
Sat, 15 Feb 2025 13:28:09 +0000 (00:28 +1100)
2158 files changed:
.gitignore
.vscode/settings.json
README.md
README.pyside6_addons.md
build_scripts/__init__.py
build_scripts/build_info_collector.py
build_scripts/config.py
build_scripts/log.py
build_scripts/main.py
build_scripts/options.py
build_scripts/platforms/__init__.py
build_scripts/platforms/linux.py
build_scripts/platforms/macos.py
build_scripts/platforms/unix.py
build_scripts/platforms/windows_desktop.py
build_scripts/qfp_tool.py
build_scripts/qtinfo.py
build_scripts/setup_runner.py
build_scripts/utils.py
build_scripts/wheel_files.py
build_scripts/wheel_override.py
build_scripts/wheel_utils.py
coin/dependencies.yaml
coin/fetch_libclang_arm64.sh [new file with mode: 0644]
coin/instructions/common_environment.yaml
coin/instructions/execute_android_instructions.yaml [new file with mode: 0644]
coin/instructions/execute_build_instructions.yaml
coin/instructions/execute_desktop_instructions.yaml [new file with mode: 0644]
coin/instructions/execute_license_check.yaml [deleted file]
coin/instructions/execute_test_instructions.yaml
coin/instructions_utils.py [new file with mode: 0644]
coin/module_config.yaml
coin_build_instructions.py
coin_test_instructions.py
create_wheels.py
doc/changelogs/changes-6.7.3 [new file with mode: 0644]
doc/changelogs/changes-6.8.0 [new file with mode: 0644]
doc/changelogs/changes-6.8.1 [new file with mode: 0644]
doc/changelogs/changes-6.8.1.1 [new file with mode: 0644]
doc/changelogs/changes-6.8.2 [new file with mode: 0644]
doc/changelogs/changes-6.8.2.1 [new file with mode: 0644]
examples/3d/simple3d/simple3d.py
examples/async/eratosthenes/eratosthenes_asyncio.py
examples/async/eratosthenes/eratosthenes_trio.py
examples/async/minimal/minimal_asyncio.py
examples/async/minimal/minimal_trio.py
examples/axcontainer/axviewer/axviewer.py
examples/bluetooth/btscanner/device.py
examples/bluetooth/btscanner/main.py
examples/bluetooth/btscanner/service.py
examples/bluetooth/btscanner/service.ui
examples/bluetooth/btscanner/ui_device.py
examples/bluetooth/btscanner/ui_service.py
examples/bluetooth/heartrate_game/bluetoothbaseclass.py
examples/bluetooth/heartrate_game/connectionhandler.py
examples/bluetooth/heartrate_game/devicefinder.py
examples/bluetooth/heartrate_game/devicehandler.py
examples/bluetooth/heartrate_game/deviceinfo.py
examples/bluetooth/heartrate_game/heartrate_global.py
examples/bluetooth/heartrate_game/main.py
examples/bluetooth/heartrate_server/heartrate_server.py
examples/bluetooth/lowenergyscanner/characteristicinfo.py
examples/bluetooth/lowenergyscanner/device.py
examples/bluetooth/lowenergyscanner/deviceinfo.py
examples/bluetooth/lowenergyscanner/main.py
examples/bluetooth/lowenergyscanner/serviceinfo.py
examples/charts/areachart/areachart.py
examples/charts/audio/audio.py
examples/charts/barchart/barchart.py
examples/charts/callout/callout.py
examples/charts/chartthemes/main.py
examples/charts/chartthemes/themewidget.ui
examples/charts/chartthemes/ui_themewidget.py
examples/charts/donutbreakdown/donutbreakdown.py
examples/charts/dynamicspline/chart.py
examples/charts/dynamicspline/main.py
examples/charts/legend/legend.py
examples/charts/lineandbar/lineandbar.py
examples/charts/linechart/linechart.py
examples/charts/logvalueaxis/logvalueaxis.py
examples/charts/memoryusage/memoryusage.py
examples/charts/modeldata/modeldata.py
examples/charts/nesteddonuts/nesteddonuts.py
examples/charts/percentbarchart/percentbarchart.py
examples/charts/piechart/piechart.py
examples/charts/pointconfiguration/chartwindow.py
examples/charts/pointconfiguration/doc/pointconfiguration.rst
examples/charts/pointconfiguration/pointconfiguration.py
examples/charts/pointselectionandmarkers/pointselectionandmarkers.py
examples/charts/pointselectionandmarkers/utilities.py
examples/charts/qmlpolarchart/qmlpolarchart.py
examples/charts/temperaturerecords/temperaturerecords.py
examples/charts/zoomlinechart/chart.py [new file with mode: 0644]
examples/charts/zoomlinechart/chartview.py [new file with mode: 0644]
examples/charts/zoomlinechart/doc/zoomlinechart.png [new file with mode: 0644]
examples/charts/zoomlinechart/doc/zoomlinechart.rst [new file with mode: 0644]
examples/charts/zoomlinechart/main.py [new file with mode: 0644]
examples/charts/zoomlinechart/zoomlinechart.pyproject [new file with mode: 0644]
examples/corelib/ipc/sharedmemory/dialog.py
examples/corelib/ipc/sharedmemory/dialog.ui
examples/corelib/ipc/sharedmemory/doc/sharedmemory.png [new file with mode: 0644]
examples/corelib/ipc/sharedmemory/doc/sharedmemory.rst [new file with mode: 0644]
examples/corelib/ipc/sharedmemory/main.py
examples/corelib/ipc/sharedmemory/ui_dialog.py
examples/corelib/mimetypesbrowser/doc/mimetypesbrowser.rst
examples/corelib/mimetypesbrowser/mainwindow.py
examples/corelib/mimetypesbrowser/mimetypemodel.py
examples/corelib/mimetypesbrowser/mimetypesbrowser.py
examples/corelib/settingseditor/settingseditor.py
examples/corelib/threads/mandelbrot.py
examples/datavisualization/bars3d/bars3d.py
examples/datavisualization/graphgallery/axesinputhandler.py
examples/datavisualization/graphgallery/bargraph.py
examples/datavisualization/graphgallery/custominputhandler.py
examples/datavisualization/graphgallery/graphmodifier.py
examples/datavisualization/graphgallery/highlightseries.py
examples/datavisualization/graphgallery/main.py
examples/datavisualization/graphgallery/rainfalldata.py
examples/datavisualization/graphgallery/scatterdatamodifier.py
examples/datavisualization/graphgallery/scattergraph.py
examples/datavisualization/graphgallery/surfacegraph.py
examples/datavisualization/graphgallery/surfacegraphmodifier.py
examples/datavisualization/graphgallery/topographicseries.py
examples/datavisualization/graphgallery/variantbardatamapping.py
examples/datavisualization/graphgallery/variantbardataproxy.py
examples/datavisualization/graphgallery/variantdataset.py
examples/datavisualization/minimalsurface/main.py
examples/datavisualization/qmlsurfacegallery/datasource.py
examples/datavisualization/qmlsurfacegallery/main.py
examples/datavisualization/surface/main.py
examples/datavisualization/surface/surfacegraph.py
examples/datavisualization/surface_model_numpy/main.py
examples/datavisualization/surface_model_numpy/surfacegraph.py
examples/datavisualization/surface_numpy/main.py
examples/datavisualization/surface_numpy/surfacegraph.py
examples/dbus/listnames/doc/listnames.rst [new file with mode: 0644]
examples/dbus/listnames/listnames.py
examples/dbus/pingpong/doc/pingpong.rst [new file with mode: 0644]
examples/dbus/pingpong/ping.py
examples/dbus/pingpong/pong.py
examples/demos/colorpaletteclient/abstractresource.py
examples/demos/colorpaletteclient/basiclogin.py
examples/demos/colorpaletteclient/main.py
examples/demos/colorpaletteclient/paginatedresource.py
examples/demos/colorpaletteclient/restservice.py
examples/demos/documentviewer/abstractviewer.py
examples/demos/documentviewer/jsonviewer/jsonviewer.py
examples/demos/documentviewer/main.py
examples/demos/documentviewer/mainwindow.py
examples/demos/documentviewer/mainwindow.ui
examples/demos/documentviewer/pdfviewer/pdfviewer.py
examples/demos/documentviewer/pdfviewer/zoomselector.py
examples/demos/documentviewer/recentfilemenu.py
examples/demos/documentviewer/recentfiles.py
examples/demos/documentviewer/txtviewer/txtviewer.py
examples/demos/documentviewer/ui_mainwindow.py
examples/demos/documentviewer/viewerfactory.py
examples/demos/osmbuildings/OSMBuildings/Main.qml [new file with mode: 0644]
examples/demos/osmbuildings/OSMBuildings/OSMCameraController.qml [new file with mode: 0644]
examples/demos/osmbuildings/OSMBuildings/customshadertiles.frag [new file with mode: 0644]
examples/demos/osmbuildings/OSMBuildings/qmldir [new file with mode: 0644]
examples/demos/osmbuildings/doc/osmbuildings.rst [new file with mode: 0644]
examples/demos/osmbuildings/doc/osmbuildings.webp [new file with mode: 0644]
examples/demos/osmbuildings/main.py [new file with mode: 0644]
examples/demos/osmbuildings/manager.py [new file with mode: 0644]
examples/demos/osmbuildings/osmbuildings.pyproject [new file with mode: 0644]
examples/demos/osmbuildings/request.py [new file with mode: 0644]
examples/designer/taskmenuextension/doc/taskmenuextension.rst
examples/designer/taskmenuextension/main.py
examples/designer/taskmenuextension/registertictactoe.py
examples/designer/taskmenuextension/tictactoe.py
examples/designer/taskmenuextension/tictactoeplugin.py
examples/designer/taskmenuextension/tictactoetaskmenu.py
examples/external/matplotlib/widget3d/widget3d.py
examples/external/matplotlib/widget_gaussian/widget_gaussian.py
examples/external/networkx/main.py
examples/external/opencv/webcam_pattern_detection.py
examples/external/pandas/dataframe_model.py
examples/external/scikit/staining_colors_separation.py
examples/graphs/2d/hellographs/HelloGraphs/Main.qml
examples/graphs/2d/hellographs/main.py
examples/graphs/3d/bars/Bars/Axes.qml [new file with mode: 0644]
examples/graphs/3d/bars/Bars/Data.qml [new file with mode: 0644]
examples/graphs/3d/bars/Bars/Main.qml [new file with mode: 0644]
examples/graphs/3d/bars/Bars/qmldir [new file with mode: 0644]
examples/graphs/3d/bars/bars.pyproject [new file with mode: 0644]
examples/graphs/3d/bars/doc/bars-example.webp [new file with mode: 0644]
examples/graphs/3d/bars/doc/bars.rst [new file with mode: 0644]
examples/graphs/3d/bars/main.py [new file with mode: 0644]
examples/graphs/3d/minimalsurfacegraph/main.py
examples/graphs/3d/minimalsurfacegraph/minimalsurfacegraph.pyproject [new file with mode: 0644]
examples/graphs/3d/widgetgallery/axesinputhandler.py [deleted file]
examples/graphs/3d/widgetgallery/bargraph.py [deleted file]
examples/graphs/3d/widgetgallery/custominputhandler.py [deleted file]
examples/graphs/3d/widgetgallery/data/layer_1.png [deleted file]
examples/graphs/3d/widgetgallery/data/layer_2.png [deleted file]
examples/graphs/3d/widgetgallery/data/layer_3.png [deleted file]
examples/graphs/3d/widgetgallery/data/license.txt [deleted file]
examples/graphs/3d/widgetgallery/data/maptexture.jpg [deleted file]
examples/graphs/3d/widgetgallery/data/narrowarrow.mesh [deleted file]
examples/graphs/3d/widgetgallery/data/oilrig.mesh [deleted file]
examples/graphs/3d/widgetgallery/data/pipe.mesh [deleted file]
examples/graphs/3d/widgetgallery/data/raindata.txt [deleted file]
examples/graphs/3d/widgetgallery/data/refinery.mesh [deleted file]
examples/graphs/3d/widgetgallery/data/topography.png [deleted file]
examples/graphs/3d/widgetgallery/doc/widgetgallery.rst [deleted file]
examples/graphs/3d/widgetgallery/doc/widgetgallery.webp [deleted file]
examples/graphs/3d/widgetgallery/graphmodifier.py [deleted file]
examples/graphs/3d/widgetgallery/highlightseries.py [deleted file]
examples/graphs/3d/widgetgallery/main.py [deleted file]
examples/graphs/3d/widgetgallery/rainfalldata.py [deleted file]
examples/graphs/3d/widgetgallery/scatterdatamodifier.py [deleted file]
examples/graphs/3d/widgetgallery/scattergraph.py [deleted file]
examples/graphs/3d/widgetgallery/surfacegraph.py [deleted file]
examples/graphs/3d/widgetgallery/surfacegraphmodifier.py [deleted file]
examples/graphs/3d/widgetgallery/topographicseries.py [deleted file]
examples/graphs/3d/widgetgallery/variantbardatamapping.py [deleted file]
examples/graphs/3d/widgetgallery/variantbardataproxy.py [deleted file]
examples/graphs/3d/widgetgallery/variantdataset.py [deleted file]
examples/graphs/3d/widgetgallery/widgetgallery.pyproject [deleted file]
examples/graphs/3d/widgetgraphgallery/bargraph.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/layer_1.png [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/layer_2.png [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/layer_3.png [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/license.txt [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/maptexture.jpg [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/narrowarrow.mesh [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/oilrig.mesh [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/pipe.mesh [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/raindata.txt [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/refinery.mesh [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/data/topography.png [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.rst [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.webp [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/graphmodifier.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/highlightseries.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/main.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/rainfalldata.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/scatterdatamodifier.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/scattergraph.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/surfacegraph.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/surfacegraphmodifier.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/topographicseries.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/variantbardatamapping.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/variantbardataproxy.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/variantdataset.py [new file with mode: 0644]
examples/graphs/3d/widgetgraphgallery/widgetgraphgallery.pyproject [new file with mode: 0644]
examples/gui/analogclock/main.py
examples/gui/rhiwindow/doc/rhiwindow.rst
examples/gui/rhiwindow/main.py
examples/gui/rhiwindow/rhiwindow.py
examples/httpserver/afterrequest/doc/afterrequest.rst [deleted file]
examples/httpserver/afterrequest/main.py [deleted file]
examples/httpserver/simplehttpserver/doc/simplehttpserver.rst [new file with mode: 0644]
examples/httpserver/simplehttpserver/main.py [new file with mode: 0644]
examples/httpserver/simplehttpserver/simplehttpserver.pyproject [new file with mode: 0644]
examples/installer_test/hello.py
examples/location/mapviewer/MapViewer/forms/MessageForm.ui.qml
examples/location/mapviewer/main.py
examples/multimedia/audiooutput/audiooutput.py
examples/multimedia/audiosource/audiosource.py
examples/multimedia/audiosource/doc/audiosource.rst
examples/multimedia/camera/camera.py
examples/multimedia/camera/camera.ui
examples/multimedia/camera/camera_mobile.ui
examples/multimedia/camera/imagesettings.py
examples/multimedia/camera/imagesettings.ui
examples/multimedia/camera/main.py
examples/multimedia/camera/metadatadialog.py
examples/multimedia/camera/ui_camera.py
examples/multimedia/camera/ui_camera_mobile.py
examples/multimedia/camera/ui_imagesettings.py
examples/multimedia/camera/ui_videosettings.py
examples/multimedia/camera/ui_videosettings_mobile.py
examples/multimedia/camera/videosettings.py
examples/multimedia/camera/videosettings.ui
examples/multimedia/camera/videosettings_mobile.ui
examples/multimedia/player/player.py
examples/multimedia/screencapture/main.py
examples/multimedia/screencapture/screencapturepreview.py
examples/multimedia/screencapture/screenlistmodel.py
examples/multimedia/screencapture/windowlistmodel.py
examples/network/blockingfortuneclient/blockingfortuneclient.py
examples/network/blockingfortuneclient/doc/blockingfortuneclient.png [new file with mode: 0644]
examples/network/blockingfortuneclient/doc/blockingfortuneclient.rst [new file with mode: 0644]
examples/network/downloader/downloader.py
examples/network/fortuneclient/doc/fortuneclient.png [new file with mode: 0644]
examples/network/fortuneclient/doc/fortuneclient.rst [new file with mode: 0644]
examples/network/fortuneclient/fortuneclient.py
examples/network/fortuneserver/doc/fortuneserver.png [new file with mode: 0644]
examples/network/fortuneserver/doc/fortuneserver.rst [new file with mode: 0644]
examples/network/fortuneserver/fortuneserver.py
examples/network/googlesuggest/googlesuggest.py
examples/network/googlesuggest/main.py
examples/network/googlesuggest/searchbox.py
examples/network/loopback/dialog.py
examples/network/loopback/main.py
examples/network/threadedfortuneserver/doc/threadedfortuneserver.png [new file with mode: 0644]
examples/network/threadedfortuneserver/doc/threadedfortuneserver.rst [new file with mode: 0644]
examples/network/threadedfortuneserver/threadedfortuneserver.py
examples/networkauth/redditclient/main.py
examples/networkauth/redditclient/redditmodel.py
examples/networkauth/redditclient/redditwrapper.py
examples/opengl/contextinfo/contextinfo.py
examples/opengl/contextinfo/requirements.txt [new file with mode: 0644]
examples/opengl/hellogl2/glwidget.py
examples/opengl/hellogl2/logo.py
examples/opengl/hellogl2/main.py
examples/opengl/hellogl2/mainwindow.py
examples/opengl/hellogl2/requirements.txt [new file with mode: 0644]
examples/opengl/hellogl2/window.py
examples/opengl/textures/requirements.txt [new file with mode: 0644]
examples/opengl/textures/textures.py
examples/opengl/threadedqopenglwidget/glwidget.py
examples/opengl/threadedqopenglwidget/main.py
examples/opengl/threadedqopenglwidget/mainwindow.py
examples/opengl/threadedqopenglwidget/renderer.py
examples/opengl/threadedqopenglwidget/requirements.txt [new file with mode: 0644]
examples/pdf/quickpdfviewer/main.py
examples/pdfwidgets/pdfviewer/main.py
examples/pdfwidgets/pdfviewer/mainwindow.py
examples/pdfwidgets/pdfviewer/mainwindow.ui
examples/pdfwidgets/pdfviewer/ui_mainwindow.py
examples/pdfwidgets/pdfviewer/zoomselector.py
examples/qml/editingmodel/main.py
examples/qml/editingmodel/model.py
examples/qml/signals/pytoqml1/main.py [deleted file]
examples/qml/signals/pytoqml1/pytoqml1.pyproject [deleted file]
examples/qml/signals/pytoqml1/view.qml [deleted file]
examples/qml/signals/pytoqml2/main.py [deleted file]
examples/qml/signals/pytoqml2/pytoqml2.pyproject [deleted file]
examples/qml/signals/pytoqml2/view.qml [deleted file]
examples/qml/signals/qmltopy1/doc/qmltopy1.rst [new file with mode: 0644]
examples/qml/signals/qmltopy1/main.py
examples/qml/signals/qmltopy2/doc/qmltopy2.rst [new file with mode: 0644]
examples/qml/signals/qmltopy2/main.py
examples/qml/signals/qmltopy3/doc/qmltopy3.rst [new file with mode: 0644]
examples/qml/signals/qmltopy3/main.py
examples/qml/signals/qmltopy4/doc/qmltopy4.rst [new file with mode: 0644]
examples/qml/signals/qmltopy4/main.py
examples/qml/textproperties/main.py
examples/qml/tutorials/extending-qml-advanced/adding/doc/adding.rst
examples/qml/tutorials/extending-qml-advanced/adding/main.py
examples/qml/tutorials/extending-qml-advanced/adding/person.py
examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/doc/advanced1-Base-project.rst
examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.py
examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.py
examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/doc/advanced2-Inheritance-and-coercion.rst
examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.py
examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.py
examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/doc/advanced3-Default-properties.rst
examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.py
examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.py
examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/doc/advanced4-Grouped-properties.rst
examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.py
examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.py
examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/doc/advanced5-Attached-properties.rst
examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py
examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.py
examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/doc/advanced6-Property-value-source.rst
examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.py
examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py
examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.py
examples/qml/tutorials/extending-qml-advanced/binding/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/binding/doc/binding.rst
examples/qml/tutorials/extending-qml-advanced/binding/happybirthdaysong.py
examples/qml/tutorials/extending-qml-advanced/binding/main.py
examples/qml/tutorials/extending-qml-advanced/binding/person.py
examples/qml/tutorials/extending-qml-advanced/extended/doc/extended.rst
examples/qml/tutorials/extending-qml-advanced/extended/main.py
examples/qml/tutorials/extending-qml-advanced/methods/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/methods/doc/methods.rst
examples/qml/tutorials/extending-qml-advanced/methods/main.py
examples/qml/tutorials/extending-qml-advanced/methods/person.py
examples/qml/tutorials/extending-qml-advanced/properties/birthdayparty.py
examples/qml/tutorials/extending-qml-advanced/properties/doc/properties.rst
examples/qml/tutorials/extending-qml-advanced/properties/main.py
examples/qml/tutorials/extending-qml-advanced/properties/person.py
examples/qml/tutorials/extending-qml/chapter1-basics/basics.py
examples/qml/tutorials/extending-qml/chapter1-basics/doc/chapter1-basics.rst
examples/qml/tutorials/extending-qml/chapter2-methods/doc/chapter2-methods.rst
examples/qml/tutorials/extending-qml/chapter2-methods/methods.py
examples/qml/tutorials/extending-qml/chapter3-bindings/bindings.py
examples/qml/tutorials/extending-qml/chapter3-bindings/doc/chapter3-bindings.rst
examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/customPropertyTypes.py
examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/doc/chapter4-customPropertyTypes.rst
examples/qml/tutorials/extending-qml/chapter5-listproperties/doc/chapter5-listproperties.rst
examples/qml/tutorials/extending-qml/chapter5-listproperties/listproperties.py
examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/piechart.py
examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/pieslice.py
examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst
examples/qml/usingmodel/usingmodel.py
examples/quick/models/objectlistmodel/objectlistmodel.py
examples/quick/models/stringlistmodel/stringlistmodel.py
examples/quick/painteditem/painteditem.py
examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.py
examples/quick/rendercontrol/rendercontrol_opengl/main.py
examples/quick/rendercontrol/rendercontrol_opengl/requirements.txt [new file with mode: 0644]
examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.py
examples/quick/scenegraph/openglunderqml/main.py
examples/quick/scenegraph/openglunderqml/requirements.txt [new file with mode: 0644]
examples/quick/scenegraph/openglunderqml/squircle.py
examples/quick/scenegraph/openglunderqml/squirclerenderer.py
examples/quick/scenegraph/scenegraph_customgeometry/main.py
examples/quick/shared/FlickrRssModel.qml
examples/quick/window/main.py
examples/quick3d/customgeometry/examplepoint.py
examples/quick3d/customgeometry/exampletriangle.py
examples/quick3d/customgeometry/main.py
examples/quick3d/intro/main.py
examples/quick3d/proceduraltexture/gradienttexture.py
examples/quick3d/proceduraltexture/main.py
examples/quickcontrols/contactslist/contactmodel.py
examples/quickcontrols/contactslist/main.py
examples/quickcontrols/filesystemexplorer/doc/filesystemexplorer.rst
examples/quickcontrols/filesystemexplorer/editormodels.py
examples/quickcontrols/filesystemexplorer/main.py
examples/quickcontrols/gallery/gallery.py
examples/remoteobjects/modelview/doc/modelview.rst [new file with mode: 0644]
examples/remoteobjects/modelview/modelviewclient.py
examples/remoteobjects/modelview/modelviewserver.py
examples/samplebinding/doc/samplebinding.pyproject
examples/samplebinding/doc/samplebinding.rst
examples/samplebinding/main.py
examples/scriptableapplication/doc/scriptableapplication.pyproject
examples/scriptableapplication/mainwindow.cpp
examples/scriptableapplication/mainwindow.h
examples/serialbus/can/bitratebox.py
examples/serialbus/can/canbusdeviceinfobox.py
examples/serialbus/can/canbusdeviceinfobox.ui
examples/serialbus/can/canbusdeviceinfodialog.py
examples/serialbus/can/canbusdeviceinfodialog.ui
examples/serialbus/can/connectdialog.py
examples/serialbus/can/connectdialog.ui
examples/serialbus/can/main.py
examples/serialbus/can/mainwindow.py
examples/serialbus/can/mainwindow.ui
examples/serialbus/can/receivedframesmodel.py
examples/serialbus/can/receivedframesview.py
examples/serialbus/can/sendframebox.py
examples/serialbus/can/sendframebox.ui
examples/serialbus/can/ui_canbusdeviceinfobox.py
examples/serialbus/can/ui_canbusdeviceinfodialog.py
examples/serialbus/can/ui_connectdialog.py
examples/serialbus/can/ui_mainwindow.py
examples/serialbus/can/ui_sendframebox.py
examples/serialbus/modbus/modbusclient/main.py
examples/serialbus/modbus/modbusclient/mainwindow.py
examples/serialbus/modbus/modbusclient/mainwindow.ui
examples/serialbus/modbus/modbusclient/settingsdialog.py
examples/serialbus/modbus/modbusclient/settingsdialog.ui
examples/serialbus/modbus/modbusclient/ui_mainwindow.py
examples/serialbus/modbus/modbusclient/ui_settingsdialog.py
examples/serialbus/modbus/modbusclient/writeregistermodel.py
examples/serialport/terminal/console.py
examples/serialport/terminal/main.py
examples/serialport/terminal/mainwindow.py
examples/serialport/terminal/mainwindow.ui
examples/serialport/terminal/settingsdialog.py
examples/serialport/terminal/settingsdialog.ui
examples/serialport/terminal/ui_mainwindow.py
examples/serialport/terminal/ui_settingsdialog.py
examples/spatialaudio/audiopanning/main.py
examples/speech/hello_speak/doc/hello_speak.rst
examples/speech/hello_speak/main.py
examples/speech/hello_speak/mainwindow.py
examples/speech/hello_speak/mainwindow.ui
examples/speech/hello_speak/ui_mainwindow.py
examples/sql/books/bookdelegate.py
examples/sql/books/bookwindow.py
examples/sql/books/bookwindow.ui
examples/sql/books/createdb.py
examples/sql/books/main.py
examples/sql/books/ui_bookwindow.py
examples/sql/relationaltablemodel/connection.py
examples/sql/relationaltablemodel/relationaltablemodel.py
examples/statemachine/moveblocks/moveblocks.py
examples/statemachine/ping_pong/doc/ping_pong.rst [new file with mode: 0644]
examples/statemachine/ping_pong/doc/pingpong.png [new file with mode: 0644]
examples/statemachine/ping_pong/ping_pong.py
examples/statemachine/rogue/doc/rogue.png [new file with mode: 0644]
examples/statemachine/rogue/doc/rogue.rst [new file with mode: 0644]
examples/statemachine/rogue/rogue.py
examples/statemachine/trafficlight/trafficlight.py
examples/tutorials/finance_manager/part1/Finance/AddDialog.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part1/Finance/FinanceDelegate.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part1/Finance/FinancePieChart.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part1/Finance/FinanceView.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part1/Finance/Main.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part1/Finance/qmldir [new file with mode: 0644]
examples/tutorials/finance_manager/part1/doc/part1.md [new file with mode: 0644]
examples/tutorials/finance_manager/part1/financemodel.py [new file with mode: 0644]
examples/tutorials/finance_manager/part1/main.py [new file with mode: 0644]
examples/tutorials/finance_manager/part1/part1.pyproject [new file with mode: 0644]
examples/tutorials/finance_manager/part2/Finance/AddDialog.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part2/Finance/FinanceDelegate.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part2/Finance/FinancePieChart.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part2/Finance/FinanceView.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part2/Finance/Main.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part2/Finance/qmldir [new file with mode: 0644]
examples/tutorials/finance_manager/part2/database.py [new file with mode: 0644]
examples/tutorials/finance_manager/part2/doc/part2.md [new file with mode: 0644]
examples/tutorials/finance_manager/part2/financemodel.py [new file with mode: 0644]
examples/tutorials/finance_manager/part2/main.py [new file with mode: 0644]
examples/tutorials/finance_manager/part2/part2.pyproject [new file with mode: 0644]
examples/tutorials/finance_manager/part2/requirements.txt [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Backend/database.py [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Backend/main.py [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Backend/rest_api.py [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/Finance/AddDialog.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/Finance/FinanceDelegate.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/Finance/FinancePieChart.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/Finance/FinanceView.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/Finance/Main.qml [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/Finance/qmldir [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/financemodel.py [new file with mode: 0644]
examples/tutorials/finance_manager/part3/Frontend/main.py [new file with mode: 0644]
examples/tutorials/finance_manager/part3/doc/part3.md [new file with mode: 0644]
examples/tutorials/finance_manager/part3/part3.pyproject [new file with mode: 0644]
examples/tutorials/finance_manager/part3/requirements.txt [new file with mode: 0644]
examples/uitools/uiloader/doc/uiloader.rst [new file with mode: 0644]
examples/uitools/uiloader/uiloader.py
examples/utils/pyside_config.py
examples/webchannel/standalone/core.py
examples/webchannel/standalone/dialog.py
examples/webchannel/standalone/main.py
examples/webchannel/standalone/ui_dialog.py
examples/webchannel/standalone/websocketclientwrapper.py
examples/webchannel/standalone/websockettransport.py
examples/webenginequick/nanobrowser/quicknanobrowser.py
examples/webenginewidgets/markdowneditor/doc/markdowneditor.png [new file with mode: 0644]
examples/webenginewidgets/markdowneditor/doc/markdowneditor.rst [new file with mode: 0644]
examples/webenginewidgets/markdowneditor/document.py
examples/webenginewidgets/markdowneditor/main.py
examples/webenginewidgets/markdowneditor/mainwindow.py
examples/webenginewidgets/markdowneditor/mainwindow.ui
examples/webenginewidgets/markdowneditor/previewpage.py
examples/webenginewidgets/markdowneditor/resources/index.html
examples/webenginewidgets/markdowneditor/ui_mainwindow.py
examples/webenginewidgets/notifications/main.py
examples/webenginewidgets/notifications/notificationpopup.py
examples/webenginewidgets/simplebrowser/browser.py
examples/webenginewidgets/simplebrowser/browserwindow.py
examples/webenginewidgets/simplebrowser/certificateerrordialog.ui
examples/webenginewidgets/simplebrowser/downloadmanagerwidget.py
examples/webenginewidgets/simplebrowser/downloadmanagerwidget.ui
examples/webenginewidgets/simplebrowser/downloadwidget.py
examples/webenginewidgets/simplebrowser/downloadwidget.ui
examples/webenginewidgets/simplebrowser/main.py
examples/webenginewidgets/simplebrowser/passworddialog.ui
examples/webenginewidgets/simplebrowser/tabwidget.py
examples/webenginewidgets/simplebrowser/ui_certificateerrordialog.py
examples/webenginewidgets/simplebrowser/ui_downloadmanagerwidget.py
examples/webenginewidgets/simplebrowser/ui_downloadwidget.py
examples/webenginewidgets/simplebrowser/ui_passworddialog.py
examples/webenginewidgets/simplebrowser/webpage.py
examples/webenginewidgets/simplebrowser/webpopupwindow.py
examples/webenginewidgets/simplebrowser/webview.py
examples/webenginewidgets/widgetsnanobrowser/widgetsnanobrowser.py
examples/webview/minibrowser/doc/minibrowser.rst [new file with mode: 0644]
examples/webview/minibrowser/doc/minibrowser.webp [new file with mode: 0644]
examples/webview/minibrowser/images/left-32.png [new file with mode: 0644]
examples/webview/minibrowser/images/refresh-32.png [new file with mode: 0644]
examples/webview/minibrowser/images/right-32.png [new file with mode: 0644]
examples/webview/minibrowser/images/settings-32.png [new file with mode: 0644]
examples/webview/minibrowser/images/stop-32.png [new file with mode: 0644]
examples/webview/minibrowser/main.py [new file with mode: 0644]
examples/webview/minibrowser/main.qml [new file with mode: 0644]
examples/webview/minibrowser/minibrowser.pyproject [new file with mode: 0644]
examples/webview/minibrowser/qml.qrc [new file with mode: 0644]
examples/webview/minibrowser/rc_qml.py [new file with mode: 0644]
examples/widgetbinding/dialog.py
examples/widgetbinding/doc/widgetbinding.pyproject
examples/widgetbinding/main.py
examples/widgetbinding/registerwigglywidget.py
examples/widgetbinding/wigglywidget.py
examples/widgets/animation/animatedtiles/animatedtiles.py
examples/widgets/animation/appchooser/appchooser.py
examples/widgets/animation/easing/easing.py
examples/widgets/animation/easing/form.ui
examples/widgets/animation/easing/ui_form.py
examples/widgets/animation/states/states.py
examples/widgets/animation/states/states_rc.py
examples/widgets/desktop/screenshot/screenshot.py
examples/widgets/desktop/systray/doc/systray.png [new file with mode: 0644]
examples/widgets/desktop/systray/doc/systray.rst [new file with mode: 0644]
examples/widgets/desktop/systray/main.py
examples/widgets/desktop/systray/window.py
examples/widgets/dialogs/classwizard/classwizard.py
examples/widgets/dialogs/classwizard/listchooser.py
examples/widgets/dialogs/extension/doc/extension.png [new file with mode: 0644]
examples/widgets/dialogs/extension/doc/extension.rst [new file with mode: 0644]
examples/widgets/dialogs/extension/extension.py
examples/widgets/dialogs/licensewizard/licensewizard.py
examples/widgets/dialogs/licensewizard/main.py
examples/widgets/dialogs/standarddialogs/doc/standarddialogs.png [new file with mode: 0644]
examples/widgets/dialogs/standarddialogs/doc/standarddialogs.rst [new file with mode: 0644]
examples/widgets/dialogs/standarddialogs/standarddialogs.py
examples/widgets/dialogs/tabdialog/tabdialog.py
examples/widgets/dialogs/trivialwizard/doc/trivialwizard.png [new file with mode: 0644]
examples/widgets/dialogs/trivialwizard/doc/trivialwizard.rst [new file with mode: 0644]
examples/widgets/dialogs/trivialwizard/trivialwizard.py
examples/widgets/draganddrop/draggableicons/draggableicons.py
examples/widgets/draganddrop/draggabletext/doc/draggabletext.png [new file with mode: 0644]
examples/widgets/draganddrop/draggabletext/doc/draggabletext.rst [new file with mode: 0644]
examples/widgets/draganddrop/draggabletext/draggabletext.py
examples/widgets/draganddrop/dropsite/droparea.py
examples/widgets/draganddrop/dropsite/dropsitewindow.py
examples/widgets/draganddrop/dropsite/main.py
examples/widgets/effects/blurpicker/blureffect.py
examples/widgets/effects/blurpicker/blurpicker.py
examples/widgets/effects/blurpicker/main.py
examples/widgets/effects/lighting/doc/lighting.rst [new file with mode: 0644]
examples/widgets/effects/lighting/doc/lighting.webp [new file with mode: 0644]
examples/widgets/effects/lighting/lighting.py
examples/widgets/gettext/doc/gettext.rst
examples/widgets/gettext/main.py
examples/widgets/graphicsview/anchorlayout/anchorlayout.py
examples/widgets/graphicsview/anchorlayout/doc/anchorlayout.rst [new file with mode: 0644]
examples/widgets/graphicsview/anchorlayout/doc/anchorlayout.webp [new file with mode: 0644]
examples/widgets/graphicsview/collidingmice/collidingmice.py
examples/widgets/graphicsview/collidingmice/doc/collidingmice.rst [new file with mode: 0644]
examples/widgets/graphicsview/collidingmice/doc/collidingmice.webp [new file with mode: 0644]
examples/widgets/graphicsview/collidingmice/mice_rc.py
examples/widgets/graphicsview/diagramscene/diagramscene.py
examples/widgets/graphicsview/diagramscene/doc/diagramscene.rst [new file with mode: 0644]
examples/widgets/graphicsview/diagramscene/doc/diagramscene.webp [new file with mode: 0644]
examples/widgets/graphicsview/dragdroprobot/doc/dragdroprobot.png [new file with mode: 0644]
examples/widgets/graphicsview/dragdroprobot/doc/dragdroprobot.rst [new file with mode: 0644]
examples/widgets/graphicsview/dragdroprobot/dragdroprobot.py
examples/widgets/graphicsview/elasticnodes/doc/elasticnodes.png [new file with mode: 0644]
examples/widgets/graphicsview/elasticnodes/doc/elasticnodes.rst [new file with mode: 0644]
examples/widgets/graphicsview/elasticnodes/elasticnodes.py
examples/widgets/imageviewer/doc/imageviewer.rst [new file with mode: 0644]
examples/widgets/imageviewer/doc/imageviewer.webp [new file with mode: 0644]
examples/widgets/imageviewer/imageviewer.py
examples/widgets/imageviewer/main.py
examples/widgets/itemviews/address_book/adddialogwidget.py
examples/widgets/itemviews/address_book/address_book.py
examples/widgets/itemviews/address_book/addresswidget.py
examples/widgets/itemviews/address_book/newaddresstab.py
examples/widgets/itemviews/address_book/tablemodel.py
examples/widgets/itemviews/basicfiltermodel/basicsortfiltermodel.py
examples/widgets/itemviews/basicfiltermodel/doc/basicfiltermodel.rst [new file with mode: 0644]
examples/widgets/itemviews/basicfiltermodel/doc/basicfiltermodel.webp [new file with mode: 0644]
examples/widgets/itemviews/dirview/dirview.py
examples/widgets/itemviews/editabletreemodel/doc/editabletreemodel.rst
examples/widgets/itemviews/editabletreemodel/main.py
examples/widgets/itemviews/editabletreemodel/mainwindow.py
examples/widgets/itemviews/editabletreemodel/treeitem.py
examples/widgets/itemviews/editabletreemodel/treemodel.py
examples/widgets/itemviews/fetchmore/doc/fetchmore.png [new file with mode: 0644]
examples/widgets/itemviews/fetchmore/doc/fetchmore.rst [new file with mode: 0644]
examples/widgets/itemviews/fetchmore/fetchmore.py
examples/widgets/itemviews/jsonmodel/jsonmodel.py
examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.py
examples/widgets/itemviews/spreadsheet/main.py
examples/widgets/itemviews/spreadsheet/spreadsheet.py
examples/widgets/itemviews/spreadsheet/spreadsheetdelegate.py
examples/widgets/itemviews/spreadsheet/spreadsheetitem.py
examples/widgets/itemviews/stardelegate/stardelegate.py
examples/widgets/itemviews/stardelegate/stareditor.py
examples/widgets/itemviews/stardelegate/starrating.py
examples/widgets/layouts/basiclayouts/basiclayouts.py
examples/widgets/layouts/borderlayout/borderlayout.py
examples/widgets/layouts/dynamiclayouts/dynamiclayouts.py
examples/widgets/layouts/flowlayout/flowlayout.py
examples/widgets/linguist/doc/linguist.rst
examples/widgets/linguist/main.py
examples/widgets/mainwindows/application/application.py
examples/widgets/mainwindows/application/doc/application.png [new file with mode: 0644]
examples/widgets/mainwindows/application/doc/application.rst [new file with mode: 0644]
examples/widgets/mainwindows/dockwidgets/dockwidgets.py
examples/widgets/mainwindows/mdi/doc/mdi.png [new file with mode: 0644]
examples/widgets/mainwindows/mdi/doc/mdi.rst [new file with mode: 0644]
examples/widgets/mainwindows/mdi/mdi.py
examples/widgets/painting/basicdrawing/basicdrawing.py
examples/widgets/painting/concentriccircles/concentriccircles.py
examples/widgets/painting/painter/painter.py
examples/widgets/painting/plot/plot.py
examples/widgets/rhi/simplerhiwidget/doc/simplerhiwidget.rst
examples/widgets/rhi/simplerhiwidget/examplewidget.py
examples/widgets/rhi/simplerhiwidget/main.py
examples/widgets/richtext/orderform/doc/orderform.rst [new file with mode: 0644]
examples/widgets/richtext/orderform/doc/orderform.webp [new file with mode: 0644]
examples/widgets/richtext/orderform/orderform.py
examples/widgets/richtext/syntaxhighlighter/syntaxhighlighter.py
examples/widgets/richtext/textedit/main.py
examples/widgets/richtext/textedit/textedit.py
examples/widgets/richtext/textobject/textobject.py
examples/widgets/thread_signals/doc/thread_signals.png [new file with mode: 0644]
examples/widgets/thread_signals/doc/thread_signals.rst [new file with mode: 0644]
examples/widgets/thread_signals/thread_signals.py
examples/widgets/tools/regularexpression/regularexpression.py
examples/widgets/tools/regularexpression/regularexpressiondialog.py
examples/widgets/tutorials/addressbook/part1.py
examples/widgets/tutorials/addressbook/part2.py
examples/widgets/tutorials/addressbook/part3.py
examples/widgets/tutorials/addressbook/part4.py
examples/widgets/tutorials/addressbook/part5.py
examples/widgets/tutorials/addressbook/part6.py
examples/widgets/tutorials/addressbook/part7.py
examples/widgets/tutorials/cannon/t1.py
examples/widgets/tutorials/cannon/t10.py
examples/widgets/tutorials/cannon/t11.py
examples/widgets/tutorials/cannon/t12.py
examples/widgets/tutorials/cannon/t13.py
examples/widgets/tutorials/cannon/t14.py
examples/widgets/tutorials/cannon/t2.py
examples/widgets/tutorials/cannon/t3.py
examples/widgets/tutorials/cannon/t4.py
examples/widgets/tutorials/cannon/t5.py
examples/widgets/tutorials/cannon/t6.py
examples/widgets/tutorials/cannon/t7.py
examples/widgets/tutorials/cannon/t8.py
examples/widgets/tutorials/cannon/t9.py
examples/widgets/tutorials/modelview/1_readonly.py
examples/widgets/tutorials/modelview/2_formatting.py
examples/widgets/tutorials/modelview/3_changingmodel.py
examples/widgets/tutorials/modelview/4_headers.py
examples/widgets/tutorials/modelview/5_edit.py
examples/widgets/tutorials/modelview/6_treeview.py
examples/widgets/tutorials/modelview/7_selections.py
examples/widgets/widgets/charactermap/characterwidget.py
examples/widgets/widgets/charactermap/fontinfodialog.py
examples/widgets/widgets/charactermap/main.py
examples/widgets/widgets/charactermap/mainwindow.py
examples/widgets/widgets/digitalclock/digitalclock.py
examples/widgets/widgets/tetrix/tetrix.py
examples/widgets/widgetsgallery/main.py
examples/widgets/widgetsgallery/widgetgallery.py
examples/xml/dombookmarks/dombookmarks.py
requirements-coin.txt [new file with mode: 0644]
requirements-doc.txt
requirements.txt
setup.py
sources/pyside-tools/CMakeLists.txt
sources/pyside-tools/android_deploy.py
sources/pyside-tools/cmake/PySideAndroid.cmake
sources/pyside-tools/deploy.py
sources/pyside-tools/deploy_lib/__init__.py
sources/pyside-tools/deploy_lib/android/__init__.py
sources/pyside-tools/deploy_lib/android/android_config.py
sources/pyside-tools/deploy_lib/android/android_helper.py
sources/pyside-tools/deploy_lib/android/android_utilities.py [new file with mode: 0644]
sources/pyside-tools/deploy_lib/android/buildozer.py
sources/pyside-tools/deploy_lib/android/recipes/PySide6/__init__.tmpl.py
sources/pyside-tools/deploy_lib/android/recipes/shiboken6/__init__.tmpl.py
sources/pyside-tools/deploy_lib/commands.py
sources/pyside-tools/deploy_lib/config.py
sources/pyside-tools/deploy_lib/default.spec
sources/pyside-tools/deploy_lib/dependency_util.py
sources/pyside-tools/deploy_lib/deploy_util.py
sources/pyside-tools/deploy_lib/nuitka_helper.py
sources/pyside-tools/deploy_lib/python_helper.py
sources/pyside-tools/metaobjectdump.py
sources/pyside-tools/project.py
sources/pyside-tools/project.pyproject
sources/pyside-tools/project/__init__.py [deleted file]
sources/pyside-tools/project/newproject.py [deleted file]
sources/pyside-tools/project/project_data.py [deleted file]
sources/pyside-tools/project/utils.py [deleted file]
sources/pyside-tools/project_lib/__init__.py [new file with mode: 0644]
sources/pyside-tools/project_lib/design_studio_project.py [new file with mode: 0644]
sources/pyside-tools/project_lib/newproject.py [new file with mode: 0644]
sources/pyside-tools/project_lib/project_data.py [new file with mode: 0644]
sources/pyside-tools/project_lib/utils.py [new file with mode: 0644]
sources/pyside-tools/pyside_tool.py
sources/pyside-tools/qml.py
sources/pyside-tools/qtpy2cpp.py
sources/pyside-tools/qtpy2cpp_lib/astdump.py
sources/pyside-tools/qtpy2cpp_lib/formatter.py
sources/pyside-tools/qtpy2cpp_lib/nodedump.py
sources/pyside-tools/qtpy2cpp_lib/qt.py
sources/pyside-tools/qtpy2cpp_lib/tests/baseline/basic_test.py
sources/pyside-tools/qtpy2cpp_lib/tests/test_qtpy2cpp.py
sources/pyside-tools/qtpy2cpp_lib/tokenizer.py
sources/pyside-tools/qtpy2cpp_lib/visitor.py
sources/pyside-tools/requirements-android.txt
sources/pyside6/.cmake.conf
sources/pyside6/PySide6/Qt3DAnimation/typesystem_3danimation.xml
sources/pyside6/PySide6/Qt3DCore/typesystem_3dcore.xml
sources/pyside6/PySide6/Qt3DExtras/typesystem_3dextras.xml
sources/pyside6/PySide6/Qt3DInput/typesystem_3dinput.xml
sources/pyside6/PySide6/Qt3DLogic/typesystem_3dlogic.xml
sources/pyside6/PySide6/Qt3DRender/typesystem_3drender.xml
sources/pyside6/PySide6/QtAsyncio/__init__.py
sources/pyside6/PySide6/QtAsyncio/events.py
sources/pyside6/PySide6/QtAsyncio/futures.py
sources/pyside6/PySide6/QtAsyncio/tasks.py
sources/pyside6/PySide6/QtCore/CMakeLists.txt
sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp
sources/pyside6/PySide6/QtCore/glue/qeasingcurve_glue.cpp
sources/pyside6/PySide6/QtCore/glue/qeasingcurve_glue.h
sources/pyside6/PySide6/QtCore/glue/qtcorehelper.cpp
sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
sources/pyside6/PySide6/QtDBus/typesystem_dbus.xml
sources/pyside6/PySide6/QtDataVisualization/typesystem_datavisualization.xml
sources/pyside6/PySide6/QtDesigner/qpydesignercustomwidgetcollection.cpp
sources/pyside6/PySide6/QtGraphs/CMakeLists.txt
sources/pyside6/PySide6/QtGraphs/typesystem_graphs.xml
sources/pyside6/PySide6/QtGraphsWidgets/CMakeLists.txt [new file with mode: 0644]
sources/pyside6/PySide6/QtGraphsWidgets/typesystem_graphswidgets.xml [new file with mode: 0644]
sources/pyside6/PySide6/QtGui/CMakeLists.txt
sources/pyside6/PySide6/QtGui/typesystem_gui_common.xml
sources/pyside6/PySide6/QtGui/typesystem_gui_rhi.xml
sources/pyside6/PySide6/QtHelp/CMakeLists.txt
sources/pyside6/PySide6/QtHelp/typesystem_help.xml
sources/pyside6/PySide6/QtHttpServer/CMakeLists.txt
sources/pyside6/PySide6/QtHttpServer/typesystem_httpserver.xml
sources/pyside6/PySide6/QtLocation/CMakeLists.txt
sources/pyside6/PySide6/QtLocation/typesystem_location.xml
sources/pyside6/PySide6/QtMultimedia/CMakeLists.txt
sources/pyside6/PySide6/QtMultimedia/typesystem_multimedia.xml
sources/pyside6/PySide6/QtMultimediaWidgets/typesystem_multimediawidgets.xml
sources/pyside6/PySide6/QtNetwork/CMakeLists.txt
sources/pyside6/PySide6/QtNetwork/typesystem_network.xml
sources/pyside6/PySide6/QtNetworkAuth/CMakeLists.txt
sources/pyside6/PySide6/QtNetworkAuth/typesystem_networkauth.xml
sources/pyside6/PySide6/QtOpenGLWidgets/typesystem_openglwidgets.xml
sources/pyside6/PySide6/QtPdfWidgets/typesystem_pdfwidgets.xml
sources/pyside6/PySide6/QtPrintSupport/typesystem_printsupport_common.xml
sources/pyside6/PySide6/QtQml/pysideqmlvolatilebool.cpp
sources/pyside6/PySide6/QtQml/typesystem_qml.xml
sources/pyside6/PySide6/QtQuick/CMakeLists.txt
sources/pyside6/PySide6/QtQuick/typesystem_quick.xml
sources/pyside6/PySide6/QtQuick3D/typesystem_quick3d.xml
sources/pyside6/PySide6/QtQuickControls2/typesystem_quickcontrols2.xml
sources/pyside6/PySide6/QtQuickWidgets/typesystem_quickwidgets.xml
sources/pyside6/PySide6/QtSerialBus/typesystem_serialbus.xml
sources/pyside6/PySide6/QtSpatialAudio/typesystem_spatialaudio.xml
sources/pyside6/PySide6/QtStateMachine/typesystem_statemachine.xml
sources/pyside6/PySide6/QtSvgWidgets/typesystem_svgwidgets.xml
sources/pyside6/PySide6/QtTest/typesystem_test.xml
sources/pyside6/PySide6/QtWebEngineCore/CMakeLists.txt
sources/pyside6/PySide6/QtWebEngineCore/glue/webengineframe.cpp [new file with mode: 0644]
sources/pyside6/PySide6/QtWebEngineCore/glue/webengineframe.h [new file with mode: 0644]
sources/pyside6/PySide6/QtWebEngineCore/glue/webenginepage_functors.cpp [new file with mode: 0644]
sources/pyside6/PySide6/QtWebEngineCore/glue/webenginepage_functors.h [new file with mode: 0644]
sources/pyside6/PySide6/QtWebEngineCore/typesystem_webenginecore.xml
sources/pyside6/PySide6/QtWebEngineQuick/typesystem_webenginequick.xml
sources/pyside6/PySide6/QtWebEngineWidgets/CMakeLists.txt
sources/pyside6/PySide6/QtWebEngineWidgets/typesystem_webenginewidgets.xml
sources/pyside6/PySide6/QtWebView/CMakeLists.txt [new file with mode: 0644]
sources/pyside6/PySide6/QtWebView/typesystem_webview.xml [new file with mode: 0644]
sources/pyside6/PySide6/QtWidgets/typesystem_widgets_common.xml
sources/pyside6/PySide6/QtXml/typesystem_xml.xml
sources/pyside6/PySide6/__init__.py.in
sources/pyside6/PySide6/doc/qtcore.rst [new file with mode: 0644]
sources/pyside6/PySide6/doc/qtqml.rst [new file with mode: 0644]
sources/pyside6/PySide6/doc/qtqml_functions.rst [deleted file]
sources/pyside6/PySide6/doc/qtwebenginecore.rst [new file with mode: 0644]
sources/pyside6/PySide6/glue/qhttpserver.cpp
sources/pyside6/PySide6/glue/qtcore.cpp
sources/pyside6/PySide6/glue/qtgui.cpp
sources/pyside6/PySide6/glue/qtnetwork.cpp
sources/pyside6/PySide6/glue/qtnetworkauth.cpp
sources/pyside6/PySide6/glue/qtquick3d.cpp [new file with mode: 0644]
sources/pyside6/PySide6/glue/qtserialbus.cpp
sources/pyside6/PySide6/glue/qtstatemachine.cpp
sources/pyside6/PySide6/glue/qttest.cpp
sources/pyside6/PySide6/glue/qtuitools.cpp
sources/pyside6/PySide6/glue/qtwebenginecore.cpp
sources/pyside6/PySide6/glue/qtwebenginewidgets.cpp [deleted file]
sources/pyside6/PySide6/glue/qtwidgets.cpp
sources/pyside6/PySide6/glue/qtxml.cpp
sources/pyside6/PySide6/qpytextobject.h
sources/pyside6/PySide6/qtcorehelper.h
sources/pyside6/PySide6/support/__init__.py
sources/pyside6/PySide6/support/deprecated.py
sources/pyside6/PySide6/support/generate_pyi.py
sources/pyside6/PySide6/templates/common.xml
sources/pyside6/PySide6/templates/core_common.xml
sources/pyside6/PySide6/templates/gui_common.xml
sources/pyside6/PySide6/templates/opengl_common.xml
sources/pyside6/PySide6/templates/widgets_common.xml
sources/pyside6/cmake/Macros/PySideModules.cmake
sources/pyside6/cmake/PySideHelpers.cmake
sources/pyside6/cmake/PySideSetup.cmake
sources/pyside6/doc/CMakeLists.txt
sources/pyside6/doc/PySide6/QtAsyncio/coroutines.png [new file with mode: 0644]
sources/pyside6/doc/PySide6/QtAsyncio/index.rst
sources/pyside6/doc/_static/css/qt_style.css
sources/pyside6/doc/_tags/android.rst
sources/pyside6/doc/additionaldocs.lst
sources/pyside6/doc/api.rst
sources/pyside6/doc/building_from_source/index.rst [new file with mode: 0644]
sources/pyside6/doc/building_from_source/linux.rst [new file with mode: 0644]
sources/pyside6/doc/building_from_source/macOS.rst [new file with mode: 0644]
sources/pyside6/doc/building_from_source/windows.rst [new file with mode: 0644]
sources/pyside6/doc/commercial/index.rst
sources/pyside6/doc/conf.py.in
sources/pyside6/doc/considerations.rst
sources/pyside6/doc/contents.rst
sources/pyside6/doc/deployment/deployment-cxfreeze.rst
sources/pyside6/doc/deployment/deployment-nuitka.rst
sources/pyside6/doc/deployment/deployment-pyside6-android-deploy.rst
sources/pyside6/doc/deployment/deployment-pyside6-deploy.rst
sources/pyside6/doc/developer/add_module.rst
sources/pyside6/doc/developer/add_port_example.rst
sources/pyside6/doc/developer/add_tool.rst
sources/pyside6/doc/developer/documentation.rst
sources/pyside6/doc/developer/index.rst
sources/pyside6/doc/developer/mypy-correctness.rst [new file with mode: 0644]
sources/pyside6/doc/developer/qtasyncio.rst [new file with mode: 0644]
sources/pyside6/doc/developer/signalmanager.md [new file with mode: 0644]
sources/pyside6/doc/developer/signature_doc.rst
sources/pyside6/doc/extras/QtCharts.rst
sources/pyside6/doc/extras/QtCore.ClassInfo.rst
sources/pyside6/doc/extras/QtCore.Property.rst
sources/pyside6/doc/extras/QtCore.QEnum.rst
sources/pyside6/doc/extras/QtCore.QFlag.rst
sources/pyside6/doc/extras/QtCore.Signal.rst
sources/pyside6/doc/extras/QtCore.Slot.rst
sources/pyside6/doc/extras/QtCore.rst
sources/pyside6/doc/extras/QtDataVisualization.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerContainerExtension.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerCustomWidgetCollection.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerMemberSheetExtension.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerTaskMenuExtension.rst
sources/pyside6/doc/extras/QtGui.rst
sources/pyside6/doc/extras/QtMultimedia.rst
sources/pyside6/doc/extras/QtNetwork.rst
sources/pyside6/doc/extras/QtOpenGL.rst
sources/pyside6/doc/extras/QtPositioning.rst
sources/pyside6/doc/extras/QtPrintSupport.rst
sources/pyside6/doc/extras/QtQml.ListProperty.rst
sources/pyside6/doc/extras/QtQml.QPyQmlParserStatus.rst
sources/pyside6/doc/extras/QtQml.QPyQmlPropertyValueSource.rst
sources/pyside6/doc/extras/QtQml.QmlAnonymous.rst
sources/pyside6/doc/extras/QtQml.QmlAttached.rst
sources/pyside6/doc/extras/QtQml.QmlElement.rst
sources/pyside6/doc/extras/QtQml.QmlExtended.rst
sources/pyside6/doc/extras/QtQml.QmlForeign.rst
sources/pyside6/doc/extras/QtQml.QmlNamedElement.rst
sources/pyside6/doc/extras/QtQml.QmlSingleton.rst
sources/pyside6/doc/extras/QtQml.QmlUncreatable.rst
sources/pyside6/doc/extras/QtQml.rst
sources/pyside6/doc/extras/QtQuick.rst [new file with mode: 0644]
sources/pyside6/doc/extras/QtQuickControls2.rst
sources/pyside6/doc/extras/QtQuickTest.rst
sources/pyside6/doc/extras/QtSensors.rst
sources/pyside6/doc/extras/QtSql.rst
sources/pyside6/doc/extras/QtWebChannel.rst
sources/pyside6/doc/extras/QtWebSockets.rst
sources/pyside6/doc/extras/QtWebView.rst [new file with mode: 0644]
sources/pyside6/doc/extras/QtWidgets.rst
sources/pyside6/doc/faq/porting_from2.rst [new file with mode: 0644]
sources/pyside6/doc/faq/typesoffiles.rst
sources/pyside6/doc/faq/whatisqt.rst
sources/pyside6/doc/gettingstarted.rst [new file with mode: 0644]
sources/pyside6/doc/gettingstarted/index.rst [deleted file]
sources/pyside6/doc/gettingstarted/linux.rst [deleted file]
sources/pyside6/doc/gettingstarted/macOS.rst [deleted file]
sources/pyside6/doc/gettingstarted/package_details.rst [deleted file]
sources/pyside6/doc/gettingstarted/packages.png [deleted file]
sources/pyside6/doc/gettingstarted/porting_from2.rst [deleted file]
sources/pyside6/doc/gettingstarted/windows.rst [deleted file]
sources/pyside6/doc/images/screenshot_hello.png [deleted file]
sources/pyside6/doc/images/screenshot_hello_quick.png [new file with mode: 0644]
sources/pyside6/doc/images/screenshot_hello_widgets.png [new file with mode: 0644]
sources/pyside6/doc/index.rst
sources/pyside6/doc/inheritance_graph.py
sources/pyside6/doc/json_inheritance.py
sources/pyside6/doc/modules.rst
sources/pyside6/doc/package_details.rst [new file with mode: 0644]
sources/pyside6/doc/packages.png [new file with mode: 0644]
sources/pyside6/doc/pyside-config.qdocconf.in
sources/pyside6/doc/qdoc_spawner.py.in
sources/pyside6/doc/qtattributionsscannertorst.py
sources/pyside6/doc/qtmodules/pyside-qtgraphs.qdocconf.in [new file with mode: 0644]
sources/pyside6/doc/qtmodules/pyside-qthttpserver.qdocconf.in [new file with mode: 0644]
sources/pyside6/doc/qtmodules/pyside-qtlocation.qdocconf.in [new file with mode: 0644]
sources/pyside6/doc/qtmodules/pyside-qtquick3d.qdocconf.in [new file with mode: 0644]
sources/pyside6/doc/qtmodules/pyside-qtstatemachine.qdocconf.in [new file with mode: 0644]
sources/pyside6/doc/qtmodules/pyside-qtwebview.qdocconf.in [new file with mode: 0644]
sources/pyside6/doc/quickstart.rst [deleted file]
sources/pyside6/doc/tools/index.rst
sources/pyside6/doc/tools/pyside-designer.rst
sources/pyside6/doc/tools/pyside-linguist.rst
sources/pyside6/doc/tools/pyside-lrelease.rst
sources/pyside6/doc/tools/pyside-lupdate.rst
sources/pyside6/doc/tools/pyside-project.rst
sources/pyside6/doc/tools/pyside-qml.rst
sources/pyside6/doc/tools/pyside-rcc.rst
sources/pyside6/doc/tools/pyside-uic.rst
sources/pyside6/doc/tools/pyside6-balsam.rst
sources/pyside6/doc/tools/pyside6-balsamui.rst
sources/pyside6/doc/tools/pyside6-qmlimportscanner.rst
sources/pyside6/doc/tools/pyside6-qsb.rst
sources/pyside6/doc/tools/pyside6-svgtoqml.rst [new file with mode: 0644]
sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst
sources/pyside6/doc/tutorials/basictutorial/dialog.rst
sources/pyside6/doc/tutorials/basictutorial/qml.rst
sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst
sources/pyside6/doc/tutorials/basictutorial/signals_and_slots.rst
sources/pyside6/doc/tutorials/basictutorial/tablewidget.rst
sources/pyside6/doc/tutorials/basictutorial/translations.rst
sources/pyside6/doc/tutorials/basictutorial/treewidget.rst
sources/pyside6/doc/tutorials/basictutorial/uifiles.rst
sources/pyside6/doc/tutorials/basictutorial/widgets.rst
sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py
sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst
sources/pyside6/doc/tutorials/datavisualize/add_chart.rst
sources/pyside6/doc/tutorials/datavisualize/add_mainwindow.rst
sources/pyside6/doc/tutorials/datavisualize/add_tableview.rst
sources/pyside6/doc/tutorials/datavisualize/datavisualize1/main.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize2/main.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize3/main_window.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_widget.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize4/main_window.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize4/table_model.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_widget.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize5/main_window.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize5/table_model.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_widget.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize6/main_window.py
sources/pyside6/doc/tutorials/datavisualize/datavisualize6/table_model.py
sources/pyside6/doc/tutorials/datavisualize/filter_data.rst
sources/pyside6/doc/tutorials/datavisualize/index.rst
sources/pyside6/doc/tutorials/datavisualize/plot_datapoints.rst
sources/pyside6/doc/tutorials/datavisualize/read_data.rst
sources/pyside6/doc/tutorials/debugging/mixed_debugging.rst
sources/pyside6/doc/tutorials/debugging/qml_debugging.rst
sources/pyside6/doc/tutorials/debugging/qtcreator/qtcreator.rst
sources/pyside6/doc/tutorials/embedded/boot2qt.md [new file with mode: 0644]
sources/pyside6/doc/tutorials/embedded/boot2qt_launcher.png [new file with mode: 0644]
sources/pyside6/doc/tutorials/expenses/expenses.rst
sources/pyside6/doc/tutorials/expenses/main.py
sources/pyside6/doc/tutorials/expenses/main_snake_prop.py
sources/pyside6/doc/tutorials/expenses/steps/01-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/02-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/03-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/04-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/05-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/06-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/07-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/08-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/09-expenses.py
sources/pyside6/doc/tutorials/expenses/steps/10-expenses.py
sources/pyside6/doc/tutorials/extendedexplorer/editormodels.py
sources/pyside6/doc/tutorials/extendedexplorer/extendedexplorer.md
sources/pyside6/doc/tutorials/extendedexplorer/main.py
sources/pyside6/doc/tutorials/extendedexplorer/scheme_manager.py
sources/pyside6/doc/tutorials/finance_manager/index.md [new file with mode: 0644]
sources/pyside6/doc/tutorials/finance_manager/part1/part1.md [new file with mode: 0644]
sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.gif [new file with mode: 0644]
sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.webp [new file with mode: 0644]
sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager_android.gif [new file with mode: 0644]
sources/pyside6/doc/tutorials/finance_manager/part2/part2.md [new file with mode: 0644]
sources/pyside6/doc/tutorials/finance_manager/part3/part3.md [new file with mode: 0644]
sources/pyside6/doc/tutorials/index.rst
sources/pyside6/doc/tutorials/modelviewprogramming/qlistview-dnd.py
sources/pyside6/doc/tutorials/modelviewprogramming/simplemodel-use.py
sources/pyside6/doc/tutorials/modelviewprogramming/stringlistmodel.py
sources/pyside6/doc/tutorials/portingguide/chapter1/createdb.py
sources/pyside6/doc/tutorials/portingguide/chapter1/main.py
sources/pyside6/doc/tutorials/portingguide/chapter2/bookdelegate.py
sources/pyside6/doc/tutorials/portingguide/chapter2/createdb.py
sources/pyside6/doc/tutorials/portingguide/chapter2/main.py
sources/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate-old.py
sources/pyside6/doc/tutorials/portingguide/chapter3/bookdelegate.py
sources/pyside6/doc/tutorials/portingguide/chapter3/bookwindow.py
sources/pyside6/doc/tutorials/portingguide/chapter3/createdb.py
sources/pyside6/doc/tutorials/portingguide/chapter3/main-old.py
sources/pyside6/doc/tutorials/portingguide/chapter3/main.py
sources/pyside6/doc/tutorials/portingguide/hello_world_ex.py
sources/pyside6/doc/tutorials/portingguide/index.rst
sources/pyside6/doc/tutorials/qmlapp/App/Main.qml [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlapp/App/logo.png [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlapp/App/qmldir [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlapp/logo.png [deleted file]
sources/pyside6/doc/tutorials/qmlapp/main.py
sources/pyside6/doc/tutorials/qmlapp/qmlapplication.rst
sources/pyside6/doc/tutorials/qmlapp/view.qml [deleted file]
sources/pyside6/doc/tutorials/qmlintegration/QmlIntegration/Main.qml [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlintegration/QmlIntegration/qmldir [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlintegration/main.py
sources/pyside6/doc/tutorials/qmlintegration/qmlintegration.rst
sources/pyside6/doc/tutorials/qmlintegration/view.qml [deleted file]
sources/pyside6/doc/tutorials/qmlsqlintegration/App/Main.qml [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlsqlintegration/App/qmldir [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlsqlintegration/Main/qmldir [new file with mode: 0644]
sources/pyside6/doc/tutorials/qmlsqlintegration/chat.qml [deleted file]
sources/pyside6/doc/tutorials/qmlsqlintegration/main.py
sources/pyside6/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst
sources/pyside6/doc/tutorials/qmlsqlintegration/sqlDialog.py
sources/pyside6/libpyside/CMakeLists.txt
sources/pyside6/libpyside/class_property.cpp
sources/pyside6/libpyside/dynamicqmetaobject.cpp
sources/pyside6/libpyside/dynamicqmetaobject.h
sources/pyside6/libpyside/dynamicslot.cpp [new file with mode: 0644]
sources/pyside6/libpyside/dynamicslot_p.h [new file with mode: 0644]
sources/pyside6/libpyside/feature_select.cpp
sources/pyside6/libpyside/globalreceiverv2.cpp [deleted file]
sources/pyside6/libpyside/globalreceiverv2.h [deleted file]
sources/pyside6/libpyside/pyside.cpp
sources/pyside6/libpyside/pyside6.pc.in
sources/pyside6/libpyside/pysideclassdecorator.cpp
sources/pyside6/libpyside/pysideclassdecorator_p.h
sources/pyside6/libpyside/pysideclassinfo.cpp
sources/pyside6/libpyside/pysidemetafunction.cpp
sources/pyside6/libpyside/pysideproperty.cpp
sources/pyside6/libpyside/pysideproperty_p.h
sources/pyside6/libpyside/pysideqenum.cpp
sources/pyside6/libpyside/pysideqslotobject_p.cpp
sources/pyside6/libpyside/pysideqslotobject_p.h
sources/pyside6/libpyside/pysidesignal.cpp
sources/pyside6/libpyside/pysidesignal.h
sources/pyside6/libpyside/pysidesignal_p.h
sources/pyside6/libpyside/pysideslot.cpp
sources/pyside6/libpyside/pysideweakref.cpp
sources/pyside6/libpyside/qobjectconnect.cpp
sources/pyside6/libpyside/qobjectconnect.h
sources/pyside6/libpyside/signalmanager.cpp
sources/pyside6/libpyside/signalmanager.h
sources/pyside6/libpysideqml/pysideqmlattached.cpp
sources/pyside6/libpysideqml/pysideqmlextended.cpp
sources/pyside6/libpysideqml/pysideqmlforeign.cpp
sources/pyside6/libpysideqml/pysideqmllistproperty.cpp
sources/pyside6/libpysideqml/pysideqmlmetacallerror.cpp
sources/pyside6/libpysideqml/pysideqmlregistertype.cpp
sources/pyside6/libpysideqml/pysideqmluncreatable.cpp
sources/pyside6/plugins/designer/CMakeLists.txt
sources/pyside6/plugins/designer/designercustomwidgets.cpp
sources/pyside6/plugins/uitools/customwidget.cpp
sources/pyside6/pyside_version.py
sources/pyside6/qtexampleicons/CMakeLists.txt
sources/pyside6/qtexampleicons/module.c
sources/pyside6/tests/Qt3DExtras/qt3dextras_test.py
sources/pyside6/tests/QtAsyncio/bug_2790.py [new file with mode: 0644]
sources/pyside6/tests/QtAsyncio/bug_2799.py [new file with mode: 0644]
sources/pyside6/tests/QtAsyncio/qasyncio_test.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_cancel_task.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_cancel_taskgroup.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_chain.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_executor.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_queues.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_threadsafe.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_time.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_uncancel.py [new file with mode: 0644]
sources/pyside6/tests/QtBluetooth/localdevice.py
sources/pyside6/tests/QtBluetooth/lowenergy_characteristics.py
sources/pyside6/tests/QtCharts/QtCharts.pyproject
sources/pyside6/tests/QtCharts/qcharts_numpy_test.py
sources/pyside6/tests/QtCharts/qcharts_test.py
sources/pyside6/tests/QtCore/attr_cache_py3k.py
sources/pyside6/tests/QtCore/blocking_signals_test.py
sources/pyside6/tests/QtCore/bug_1019.py
sources/pyside6/tests/QtCore/bug_1063.py
sources/pyside6/tests/QtCore/bug_1069.py
sources/pyside6/tests/QtCore/bug_1313.py
sources/pyside6/tests/QtCore/bug_278_test.py
sources/pyside6/tests/QtCore/bug_300_test.py
sources/pyside6/tests/QtCore/bug_324.py
sources/pyside6/tests/QtCore/bug_332.py
sources/pyside6/tests/QtCore/bug_408.py
sources/pyside6/tests/QtCore/bug_428.py
sources/pyside6/tests/QtCore/bug_462.py
sources/pyside6/tests/QtCore/bug_505.py
sources/pyside6/tests/QtCore/bug_515.py
sources/pyside6/tests/QtCore/bug_606.py
sources/pyside6/tests/QtCore/bug_656.py
sources/pyside6/tests/QtCore/bug_686.py
sources/pyside6/tests/QtCore/bug_699.py
sources/pyside6/tests/QtCore/bug_706.py
sources/pyside6/tests/QtCore/bug_820.py
sources/pyside6/tests/QtCore/bug_826.py
sources/pyside6/tests/QtCore/bug_829.py
sources/pyside6/tests/QtCore/bug_835.py
sources/pyside6/tests/QtCore/bug_920.py
sources/pyside6/tests/QtCore/bug_927.py
sources/pyside6/tests/QtCore/bug_931.py
sources/pyside6/tests/QtCore/bug_938.py
sources/pyside6/tests/QtCore/bug_953.py
sources/pyside6/tests/QtCore/bug_987.py
sources/pyside6/tests/QtCore/bug_994.py
sources/pyside6/tests/QtCore/bug_PYSIDE-164.py
sources/pyside6/tests/QtCore/bug_PYSIDE-2745.py
sources/pyside6/tests/QtCore/bug_PYSIDE-41.py
sources/pyside6/tests/QtCore/bug_PYSIDE-42.py
sources/pyside6/tests/QtCore/child_event_test.py
sources/pyside6/tests/QtCore/classinfo_test.py
sources/pyside6/tests/QtCore/deepcopy_test.py
sources/pyside6/tests/QtCore/deletelater_test.py
sources/pyside6/tests/QtCore/destroysignal_test.py
sources/pyside6/tests/QtCore/duck_punching_test.py
sources/pyside6/tests/QtCore/emoji_string_test.py
sources/pyside6/tests/QtCore/errormessages_with_features_test.py
sources/pyside6/tests/QtCore/feature_with_uic/window.py
sources/pyside6/tests/QtCore/feature_with_uic/window.ui
sources/pyside6/tests/QtCore/feature_with_uic_test.py
sources/pyside6/tests/QtCore/hash_test.py
sources/pyside6/tests/QtCore/inherits_test.py
sources/pyside6/tests/QtCore/loggingcategorymacros_test.py
sources/pyside6/tests/QtCore/max_signals.py
sources/pyside6/tests/QtCore/missing_symbols_test.py
sources/pyside6/tests/QtCore/mockclass_test.py
sources/pyside6/tests/QtCore/multiple_feature_test.py
sources/pyside6/tests/QtCore/python_conversion.py
sources/pyside6/tests/QtCore/qabs_test.py
sources/pyside6/tests/QtCore/qabstractitemmodel_test.py
sources/pyside6/tests/QtCore/qanimationgroup_test.py
sources/pyside6/tests/QtCore/qbitarray_test.py
sources/pyside6/tests/QtCore/qbytearray_concatenation_operator_test.py
sources/pyside6/tests/QtCore/qbytearray_operator_iadd_test.py
sources/pyside6/tests/QtCore/qbytearray_operator_test.py
sources/pyside6/tests/QtCore/qbytearray_test.py
sources/pyside6/tests/QtCore/qcalendar_test.py
sources/pyside6/tests/QtCore/qcbor_test.py
sources/pyside6/tests/QtCore/qcollator_test.py
sources/pyside6/tests/QtCore/qcommandlineparser_test.py
sources/pyside6/tests/QtCore/qcoreapplication_argv_test.py
sources/pyside6/tests/QtCore/qcoreapplication_instance_test.py
sources/pyside6/tests/QtCore/qcoreapplication_test.py
sources/pyside6/tests/QtCore/qdatastream_test.py
sources/pyside6/tests/QtCore/qdate_test.py
sources/pyside6/tests/QtCore/qdatetime_test.py
sources/pyside6/tests/QtCore/qdir_test.py
sources/pyside6/tests/QtCore/qeasingcurve_test.py
sources/pyside6/tests/QtCore/qenum_test.py
sources/pyside6/tests/QtCore/qevent_test.py
sources/pyside6/tests/QtCore/qfile_test.py
sources/pyside6/tests/QtCore/qfileinfo_test.py
sources/pyside6/tests/QtCore/qfileread_test.py
sources/pyside6/tests/QtCore/qflags_test.py
sources/pyside6/tests/QtCore/qhandle_test.py
sources/pyside6/tests/QtCore/qinstallmsghandler_test.py
sources/pyside6/tests/QtCore/qiodevice_buffered_read_test.py
sources/pyside6/tests/QtCore/qiopipe_test.py
sources/pyside6/tests/QtCore/qjsondocument_test.py
sources/pyside6/tests/QtCore/qlinef_test.py
sources/pyside6/tests/QtCore/qlocale_test.py
sources/pyside6/tests/QtCore/qlockfile_test.py
sources/pyside6/tests/QtCore/qmessageauthenticationcode_test.py
sources/pyside6/tests/QtCore/qmetaobject_test.py
sources/pyside6/tests/QtCore/qmetatype_test.py
sources/pyside6/tests/QtCore/qmimedatabase_test.py
sources/pyside6/tests/QtCore/qmodelindex_internalpointer_test.py
sources/pyside6/tests/QtCore/qobject_children_segfault_test.py
sources/pyside6/tests/QtCore/qobject_connect_notify_test.py
sources/pyside6/tests/QtCore/qobject_destructor.py
sources/pyside6/tests/QtCore/qobject_event_filter_test.py
sources/pyside6/tests/QtCore/qobject_inherits_test.py
sources/pyside6/tests/QtCore/qobject_objectproperty_test.py
sources/pyside6/tests/QtCore/qobject_parent_test.py
sources/pyside6/tests/QtCore/qobject_property_test.py
sources/pyside6/tests/QtCore/qobject_protected_methods_test.py
sources/pyside6/tests/QtCore/qobject_test.py
sources/pyside6/tests/QtCore/qobject_timer_event_test.py
sources/pyside6/tests/QtCore/qobject_tr_as_instance_test.py
sources/pyside6/tests/QtCore/qoperatingsystemversion_test.py
sources/pyside6/tests/QtCore/qpoint_test.py
sources/pyside6/tests/QtCore/qprocess_test.py
sources/pyside6/tests/QtCore/qproperty_decorator.py
sources/pyside6/tests/QtCore/qrandomgenerator_test.py
sources/pyside6/tests/QtCore/qrect_test.py
sources/pyside6/tests/QtCore/qregularexpression_test.py
sources/pyside6/tests/QtCore/qresource_test.py
sources/pyside6/tests/QtCore/qrunnable_test.py
sources/pyside6/tests/QtCore/qsettings_test.ini
sources/pyside6/tests/QtCore/qsettings_test.py
sources/pyside6/tests/QtCore/qsharedmemory_client.py
sources/pyside6/tests/QtCore/qsharedmemory_test.py
sources/pyside6/tests/QtCore/qsize_test.py
sources/pyside6/tests/QtCore/qslot_object_test.py
sources/pyside6/tests/QtCore/qsocketnotifier_test.py
sources/pyside6/tests/QtCore/qsrand_test.py
sources/pyside6/tests/QtCore/qstandardpaths_test.py
sources/pyside6/tests/QtCore/qstorageinfo_test.py
sources/pyside6/tests/QtCore/qstring_test.py
sources/pyside6/tests/QtCore/qsysinfo_test.py
sources/pyside6/tests/QtCore/qtextstream_test.py
sources/pyside6/tests/QtCore/qthread_prod_cons_test.py
sources/pyside6/tests/QtCore/qthread_signal_test.py
sources/pyside6/tests/QtCore/qthread_test.py
sources/pyside6/tests/QtCore/qtimer_singleshot_test.py
sources/pyside6/tests/QtCore/qtimer_timeout_test.py
sources/pyside6/tests/QtCore/qtimezone_test.py
sources/pyside6/tests/QtCore/qtnamespace_test.py
sources/pyside6/tests/QtCore/qurl_test.py
sources/pyside6/tests/QtCore/qurlquery_test.py
sources/pyside6/tests/QtCore/quuid_test.py
sources/pyside6/tests/QtCore/qversionnumber_test.py
sources/pyside6/tests/QtCore/repr_test.py
sources/pyside6/tests/QtCore/resources.qrc
sources/pyside6/tests/QtCore/resources_mc.py
sources/pyside6/tests/QtCore/setprop_on_ctor_test.py
sources/pyside6/tests/QtCore/signal_sender.py
sources/pyside6/tests/QtCore/snake_prop_feature_test.py
sources/pyside6/tests/QtCore/staticMetaObject_test.py
sources/pyside6/tests/QtCore/static_method_test.py
sources/pyside6/tests/QtCore/thread_signals_test.py
sources/pyside6/tests/QtCore/tr_noop_test.py
sources/pyside6/tests/QtCore/translation_test.py
sources/pyside6/tests/QtCore/unaryoperator_test.py
sources/pyside6/tests/QtCore/unicode_test.py
sources/pyside6/tests/QtCore/versioninfo_test.py
sources/pyside6/tests/QtDBus/test_dbus.py
sources/pyside6/tests/QtDataVisualization/datavisualization_test.py
sources/pyside6/tests/QtGraphs/CMakeLists.txt
sources/pyside6/tests/QtGraphs/QtGraphs.pyproject [new file with mode: 0644]
sources/pyside6/tests/QtGraphs/qgraphs_numpy_test.py [new file with mode: 0644]
sources/pyside6/tests/QtGraphsWidgets/CMakeLists.txt [new file with mode: 0644]
sources/pyside6/tests/QtGui/bug_1091.py
sources/pyside6/tests/QtGui/bug_367.py
sources/pyside6/tests/QtGui/bug_493.py
sources/pyside6/tests/QtGui/bug_606.py
sources/pyside6/tests/QtGui/bug_617.py
sources/pyside6/tests/QtGui/bug_652.py
sources/pyside6/tests/QtGui/bug_660.py
sources/pyside6/tests/QtGui/bug_716.py
sources/pyside6/tests/QtGui/bug_740.py
sources/pyside6/tests/QtGui/bug_743.py
sources/pyside6/tests/QtGui/bug_785.py
sources/pyside6/tests/QtGui/bug_991.py
sources/pyside6/tests/QtGui/bug_PYSIDE-344.py
sources/pyside6/tests/QtGui/deepcopy_test.py
sources/pyside6/tests/QtGui/event_filter_test.py
sources/pyside6/tests/QtGui/float_to_int_implicit_conversion_test.py
sources/pyside6/tests/QtGui/pyside_reload_test.py
sources/pyside6/tests/QtGui/qbrush_test.py
sources/pyside6/tests/QtGui/qcolor_reduce_test.py
sources/pyside6/tests/QtGui/qcolor_test.py
sources/pyside6/tests/QtGui/qcursor_test.py
sources/pyside6/tests/QtGui/qdatastream_gui_operators_test.py
sources/pyside6/tests/QtGui/qdesktopservices_test.py
sources/pyside6/tests/QtGui/qfont_test.py
sources/pyside6/tests/QtGui/qfontmetrics_test.py
sources/pyside6/tests/QtGui/qguiapplication_test.py
sources/pyside6/tests/QtGui/qicon_test.py
sources/pyside6/tests/QtGui/qimage_test.py
sources/pyside6/tests/QtGui/qimage_win_test.py
sources/pyside6/tests/QtGui/qitemselection_test.py
sources/pyside6/tests/QtGui/qkeysequence_test.py
sources/pyside6/tests/QtGui/qpainter_test.py
sources/pyside6/tests/QtGui/qpdfwriter_test.py
sources/pyside6/tests/QtGui/qpen_test.py
sources/pyside6/tests/QtGui/qpixelformat_test.py
sources/pyside6/tests/QtGui/qpixmap_constructor.py
sources/pyside6/tests/QtGui/qpixmap_test.py
sources/pyside6/tests/QtGui/qpixmapcache_test.py
sources/pyside6/tests/QtGui/qpolygonf_test.py
sources/pyside6/tests/QtGui/qradialgradient_test.py
sources/pyside6/tests/QtGui/qrasterwindow_test.py
sources/pyside6/tests/QtGui/qregion_test.py
sources/pyside6/tests/QtGui/qshortcut_test.py
sources/pyside6/tests/QtGui/qstandarditemmodel_test.py
sources/pyside6/tests/QtGui/qstring_qkeysequence_test.py
sources/pyside6/tests/QtGui/qstylehints_test.py
sources/pyside6/tests/QtGui/qtextdocument_functions.py
sources/pyside6/tests/QtGui/qtextdocument_undoredo_test.py
sources/pyside6/tests/QtGui/qtextdocumentwriter_test.py
sources/pyside6/tests/QtGui/qtextline_test.py
sources/pyside6/tests/QtGui/qtransform_test.py
sources/pyside6/tests/QtGui/repr_test.py
sources/pyside6/tests/QtGui/timed_app_and_patching_test.py
sources/pyside6/tests/QtGui/xpm_data.py
sources/pyside6/tests/QtHelp/help_test.py
sources/pyside6/tests/QtHelp/helpsearchengine_test.py
sources/pyside6/tests/QtLocation/location.py
sources/pyside6/tests/QtMultimedia/audio_test.py
sources/pyside6/tests/QtMultimediaWidgets/qmultimediawidgets.py
sources/pyside6/tests/QtNetwork/accessManager_test.py
sources/pyside6/tests/QtNetwork/bug_1084.py
sources/pyside6/tests/QtNetwork/bug_446.py
sources/pyside6/tests/QtNetwork/dnslookup_test.py
sources/pyside6/tests/QtNetwork/qhostinfo_test.py
sources/pyside6/tests/QtNetwork/qipv6address_test.py
sources/pyside6/tests/QtNetwork/qpassworddigestor_test.py
sources/pyside6/tests/QtNetwork/tcpserver_test.py
sources/pyside6/tests/QtNetwork/udpsocket_test.py
sources/pyside6/tests/QtNfc/test_nfc.py
sources/pyside6/tests/QtOpenGL/qopenglbuffer_test.py
sources/pyside6/tests/QtOpenGL/qopenglwindow_test.py
sources/pyside6/tests/QtPositioning/positioning.py
sources/pyside6/tests/QtPrintSupport/bug_500.py
sources/pyside6/tests/QtPrintSupport/returnquadruplesofnumbers_test.py
sources/pyside6/tests/QtQml/bug_1029.py
sources/pyside6/tests/QtQml/bug_451.py
sources/pyside6/tests/QtQml/bug_456.py
sources/pyside6/tests/QtQml/bug_557.py
sources/pyside6/tests/QtQml/bug_726.py
sources/pyside6/tests/QtQml/bug_814.py
sources/pyside6/tests/QtQml/bug_814.qml
sources/pyside6/tests/QtQml/bug_825.py
sources/pyside6/tests/QtQml/bug_825_old.py
sources/pyside6/tests/QtQml/bug_847.py
sources/pyside6/tests/QtQml/bug_847.qml
sources/pyside6/tests/QtQml/bug_915.py
sources/pyside6/tests/QtQml/bug_926.py
sources/pyside6/tests/QtQml/bug_951.py
sources/pyside6/tests/QtQml/bug_995.py
sources/pyside6/tests/QtQml/bug_997.py
sources/pyside6/tests/QtQml/connect_python_qml.py
sources/pyside6/tests/QtQml/groupedproperty.py
sources/pyside6/tests/QtQml/javascript_exceptions.py
sources/pyside6/tests/QtQml/listproperty.py
sources/pyside6/tests/QtQml/qmlregistertype_test.py
sources/pyside6/tests/QtQml/qqmlapplicationengine_test.py
sources/pyside6/tests/QtQml/qqmlcomponent_test.py
sources/pyside6/tests/QtQml/qqmlincubator_incubateWhile.py
sources/pyside6/tests/QtQml/qqmlnetwork_test.py
sources/pyside6/tests/QtQml/qquickitem_grabToImage.py
sources/pyside6/tests/QtQml/qquickview_test.py
sources/pyside6/tests/QtQml/registerattached.py
sources/pyside6/tests/QtQml/registerextended.py
sources/pyside6/tests/QtQml/registerforeign.py
sources/pyside6/tests/QtQml/registerparserstatus.py
sources/pyside6/tests/QtQml/registerqmlfile.py
sources/pyside6/tests/QtQml/registersingletontype.py
sources/pyside6/tests/QtQml/registertype.py
sources/pyside6/tests/QtQml/registeruncreatabletype.py
sources/pyside6/tests/QtQml/signal_arguments.py
sources/pyside6/tests/QtQml/signal_types.py
sources/pyside6/tests/QtQml/viewmodel.qml
sources/pyside6/tests/QtQuickTest/quicktestmainwithsetup/tst_quicktestmainwithsetup.py
sources/pyside6/tests/QtScxml/test_dynamic.py
sources/pyside6/tests/QtSensors/sensors.py
sources/pyside6/tests/QtSerialPort/serial.py
sources/pyside6/tests/QtSql/bug_1013.py
sources/pyside6/tests/QtSql/qsqldatabaseandqueries_test.py
sources/pyside6/tests/QtSql/qvarianttype_test.py
sources/pyside6/tests/QtStateMachine/bug_1031.py
sources/pyside6/tests/QtStateMachine/qabstracttransition_test.py
sources/pyside6/tests/QtStateMachine/qstate_test.py
sources/pyside6/tests/QtStateMachine/qstatemachine_test.py
sources/pyside6/tests/QtStateMachine/setprop_on_ctor_test.py
sources/pyside6/tests/QtSvg/qsvggenerator_test.py
sources/pyside6/tests/QtSvg/qsvgrenderer_test.py
sources/pyside6/tests/QtSvgWidgets/qsvgwidget_test.py
sources/pyside6/tests/QtTest/click_test.py
sources/pyside6/tests/QtTest/eventfilter_test.py
sources/pyside6/tests/QtTest/qsignalspy_test.py
sources/pyside6/tests/QtTest/qvalidator_test.py
sources/pyside6/tests/QtTest/touchevent_test.py
sources/pyside6/tests/QtTextToSpeech/qtexttospeech_test.py
sources/pyside6/tests/QtUiTools/action.ui
sources/pyside6/tests/QtUiTools/bug_1060.py
sources/pyside6/tests/QtUiTools/bug_360.py
sources/pyside6/tests/QtUiTools/bug_376.py
sources/pyside6/tests/QtUiTools/bug_392.py
sources/pyside6/tests/QtUiTools/bug_426.py
sources/pyside6/tests/QtUiTools/bug_552.py
sources/pyside6/tests/QtUiTools/bug_797.py
sources/pyside6/tests/QtUiTools/bug_909.py
sources/pyside6/tests/QtUiTools/bug_909.ui
sources/pyside6/tests/QtUiTools/bug_913.py
sources/pyside6/tests/QtUiTools/bug_958.py
sources/pyside6/tests/QtUiTools/bug_965.py
sources/pyside6/tests/QtUiTools/bug_965.ui
sources/pyside6/tests/QtUiTools/loadUiType_test.py
sources/pyside6/tests/QtUiTools/minimal.ui
sources/pyside6/tests/QtUiTools/pycustomwidget2.ui
sources/pyside6/tests/QtUiTools/test.ui
sources/pyside6/tests/QtUiTools/ui_test.py
sources/pyside6/tests/QtUiTools/uiloader_test.py
sources/pyside6/tests/QtWebEngineCore/qwebenginecookiestore_test.py
sources/pyside6/tests/QtWebEngineCore/web_engine_custom_scheme.py
sources/pyside6/tests/QtWebEngineWidgets/pyside-474-qtwebengineview.py
sources/pyside6/tests/QtWebView/CMakeLists.txt [new file with mode: 0644]
sources/pyside6/tests/QtWebView/QtWebView.pyproject [new file with mode: 0644]
sources/pyside6/tests/QtWebView/test_webview.py [new file with mode: 0644]
sources/pyside6/tests/QtWidgets/action_clear.py
sources/pyside6/tests/QtWidgets/add_action_test.py
sources/pyside6/tests/QtWidgets/api2_test.py
sources/pyside6/tests/QtWidgets/application_test.py
sources/pyside6/tests/QtWidgets/bug_1002.py
sources/pyside6/tests/QtWidgets/bug_1006.py
sources/pyside6/tests/QtWidgets/bug_1048.py
sources/pyside6/tests/QtWidgets/bug_1077.py
sources/pyside6/tests/QtWidgets/bug_172.py
sources/pyside6/tests/QtWidgets/bug_243.py
sources/pyside6/tests/QtWidgets/bug_307.py
sources/pyside6/tests/QtWidgets/bug_338.py
sources/pyside6/tests/QtWidgets/bug_389.py
sources/pyside6/tests/QtWidgets/bug_400.py
sources/pyside6/tests/QtWidgets/bug_429.py
sources/pyside6/tests/QtWidgets/bug_430.py
sources/pyside6/tests/QtWidgets/bug_433.py
sources/pyside6/tests/QtWidgets/bug_467.py
sources/pyside6/tests/QtWidgets/bug_480.py
sources/pyside6/tests/QtWidgets/bug_512.py
sources/pyside6/tests/QtWidgets/bug_525.py
sources/pyside6/tests/QtWidgets/bug_546.py
sources/pyside6/tests/QtWidgets/bug_547.py
sources/pyside6/tests/QtWidgets/bug_549.py
sources/pyside6/tests/QtWidgets/bug_569.py
sources/pyside6/tests/QtWidgets/bug_575.py
sources/pyside6/tests/QtWidgets/bug_576.py
sources/pyside6/tests/QtWidgets/bug_585.py
sources/pyside6/tests/QtWidgets/bug_589.py
sources/pyside6/tests/QtWidgets/bug_635.py
sources/pyside6/tests/QtWidgets/bug_640.py
sources/pyside6/tests/QtWidgets/bug_653.py
sources/pyside6/tests/QtWidgets/bug_662.py
sources/pyside6/tests/QtWidgets/bug_667.py
sources/pyside6/tests/QtWidgets/bug_668.py
sources/pyside6/tests/QtWidgets/bug_674.py
sources/pyside6/tests/QtWidgets/bug_675.py
sources/pyside6/tests/QtWidgets/bug_688.py
sources/pyside6/tests/QtWidgets/bug_693.py
sources/pyside6/tests/QtWidgets/bug_696.py
sources/pyside6/tests/QtWidgets/bug_711.py
sources/pyside6/tests/QtWidgets/bug_722.py
sources/pyside6/tests/QtWidgets/bug_728.py
sources/pyside6/tests/QtWidgets/bug_736.py
sources/pyside6/tests/QtWidgets/bug_750.py
sources/pyside6/tests/QtWidgets/bug_778.py
sources/pyside6/tests/QtWidgets/bug_793.py
sources/pyside6/tests/QtWidgets/bug_811.py
sources/pyside6/tests/QtWidgets/bug_834.py
sources/pyside6/tests/QtWidgets/bug_836.py
sources/pyside6/tests/QtWidgets/bug_844.py
sources/pyside6/tests/QtWidgets/bug_854.py
sources/pyside6/tests/QtWidgets/bug_860.py
sources/pyside6/tests/QtWidgets/bug_862.py
sources/pyside6/tests/QtWidgets/bug_871.py
sources/pyside6/tests/QtWidgets/bug_879.py
sources/pyside6/tests/QtWidgets/bug_919.py
sources/pyside6/tests/QtWidgets/bug_921.py
sources/pyside6/tests/QtWidgets/bug_941.py
sources/pyside6/tests/QtWidgets/bug_964.py
sources/pyside6/tests/QtWidgets/bug_967.py
sources/pyside6/tests/QtWidgets/bug_972.py
sources/pyside6/tests/QtWidgets/bug_979.py
sources/pyside6/tests/QtWidgets/bug_988.py
sources/pyside6/tests/QtWidgets/bug_998.py
sources/pyside6/tests/QtWidgets/customproxywidget_test.py
sources/pyside6/tests/QtWidgets/grandparent_method_test.py
sources/pyside6/tests/QtWidgets/hashabletype_test.py
sources/pyside6/tests/QtWidgets/import_test.py
sources/pyside6/tests/QtWidgets/keep_reference_test.py
sources/pyside6/tests/QtWidgets/missing_symbols_test.py
sources/pyside6/tests/QtWidgets/paint_event_test.py
sources/pyside6/tests/QtWidgets/parent_method_test.py
sources/pyside6/tests/QtWidgets/private_mangle_test.py
sources/pyside6/tests/QtWidgets/python_properties_test.py
sources/pyside6/tests/QtWidgets/qabstracttextdocumentlayout_test.py
sources/pyside6/tests/QtWidgets/qaccessible_test.py
sources/pyside6/tests/QtWidgets/qaction_test.py
sources/pyside6/tests/QtWidgets/qapp_issue_585.py
sources/pyside6/tests/QtWidgets/qapp_test.py
sources/pyside6/tests/QtWidgets/qapplication_exit_segfault_test.py
sources/pyside6/tests/QtWidgets/qapplication_test.py
sources/pyside6/tests/QtWidgets/qcolormap_test.py
sources/pyside6/tests/QtWidgets/qdialog_test.py
sources/pyside6/tests/QtWidgets/qdynamic_signal.py
sources/pyside6/tests/QtWidgets/qfontdialog_test.py
sources/pyside6/tests/QtWidgets/qformlayout_test.py
sources/pyside6/tests/QtWidgets/qgraphicsitem_isblocked_test.py
sources/pyside6/tests/QtWidgets/qgraphicsitem_test.py
sources/pyside6/tests/QtWidgets/qgraphicsobjectreimpl_test.py
sources/pyside6/tests/QtWidgets/qgraphicsproxywidget_test.py
sources/pyside6/tests/QtWidgets/qgraphicsscene_test.py
sources/pyside6/tests/QtWidgets/qinputdialog_get_test.py
sources/pyside6/tests/QtWidgets/qkeysequenceedit_test.py
sources/pyside6/tests/QtWidgets/qlabel_test.py
sources/pyside6/tests/QtWidgets/qlayout_ref_test.py
sources/pyside6/tests/QtWidgets/qlayout_test.py
sources/pyside6/tests/QtWidgets/qlcdnumber_test.py
sources/pyside6/tests/QtWidgets/qlistwidget_test.py
sources/pyside6/tests/QtWidgets/qlistwidgetitem_test.py
sources/pyside6/tests/QtWidgets/qmainwindow_test.py
sources/pyside6/tests/QtWidgets/qmenu_test.py
sources/pyside6/tests/QtWidgets/qmenuadd_test.py
sources/pyside6/tests/QtWidgets/qobject_mi_test.py
sources/pyside6/tests/QtWidgets/qpicture_test.py
sources/pyside6/tests/QtWidgets/qpushbutton_test.py
sources/pyside6/tests/QtWidgets/qsplitter_test.py
sources/pyside6/tests/QtWidgets/qstyle_test.py
sources/pyside6/tests/QtWidgets/qstyleoption_test.py
sources/pyside6/tests/QtWidgets/qtableview_test.py
sources/pyside6/tests/QtWidgets/qtabwidget_test.py
sources/pyside6/tests/QtWidgets/qtabwidgetclear_test.py
sources/pyside6/tests/QtWidgets/qtextedit_signal_test.py
sources/pyside6/tests/QtWidgets/qtextedit_test.py
sources/pyside6/tests/QtWidgets/qtoolbar_test.py
sources/pyside6/tests/QtWidgets/qtoolbox_test.py
sources/pyside6/tests/QtWidgets/qtreeview_test.py
sources/pyside6/tests/QtWidgets/qtreewidget_test.py
sources/pyside6/tests/QtWidgets/qtreewidgetitem_test.py
sources/pyside6/tests/QtWidgets/qvariant_test.py
sources/pyside6/tests/QtWidgets/qwidget_setlayout_test.py
sources/pyside6/tests/QtWidgets/qwidget_test.py
sources/pyside6/tests/QtWidgets/reference_count_test.py
sources/pyside6/tests/QtWidgets/signature_test.py
sources/pyside6/tests/QtWidgets/standardpixmap_test.py
sources/pyside6/tests/QtWidgets/test_module_template.py
sources/pyside6/tests/QtWidgets/virtual_protected_inheritance_test.py
sources/pyside6/tests/QtWidgets/virtual_pure_override_test.py
sources/pyside6/tests/QtWidgets/wrong_return_test.py
sources/pyside6/tests/QtXml/qdomdocument_test.py
sources/pyside6/tests/init_paths.py
sources/pyside6/tests/mac/qmacstyle_test.py
sources/pyside6/tests/manually/bug_841.py
sources/pyside6/tests/manually/lazytiming.py
sources/pyside6/tests/manually/sizebench.py [new file with mode: 0644]
sources/pyside6/tests/pysidetest/CMakeLists.txt
sources/pyside6/tests/pysidetest/all_modules_load_test.py
sources/pyside6/tests/pysidetest/bug_1016.py
sources/pyside6/tests/pysidetest/connection.h [new file with mode: 0644]
sources/pyside6/tests/pysidetest/constructor_properties_test.py
sources/pyside6/tests/pysidetest/container_test.py
sources/pyside6/tests/pysidetest/decoratedslot_test.py
sources/pyside6/tests/pysidetest/delegatecreateseditor_test.py
sources/pyside6/tests/pysidetest/enum_test.py
sources/pyside6/tests/pysidetest/homonymoussignalandmethod_test.py
sources/pyside6/tests/pysidetest/iterable_test.py
sources/pyside6/tests/pysidetest/list_signal_test.py
sources/pyside6/tests/pysidetest/mixin_signal_slots_test.py
sources/pyside6/tests/pysidetest/mock_as_slot_test.py
sources/pyside6/tests/pysidetest/modelview_test.py
sources/pyside6/tests/pysidetest/multiple_inheritance_test.py
sources/pyside6/tests/pysidetest/mypy_correctness_test.py [new file with mode: 0644]
sources/pyside6/tests/pysidetest/new_inherited_functions_test.py
sources/pyside6/tests/pysidetest/notify_id.py
sources/pyside6/tests/pysidetest/properties_test.py
sources/pyside6/tests/pysidetest/property_python_test.py
sources/pyside6/tests/pysidetest/pyenum_relax_options_and_lazy_star_test.py [new file with mode: 0644]
sources/pyside6/tests/pysidetest/pyenum_relax_options_test.py [deleted file]
sources/pyside6/tests/pysidetest/pysidetest_global.h
sources/pyside6/tests/pysidetest/qapp_like_a_macro_test.py
sources/pyside6/tests/pysidetest/qvariant_test.py
sources/pyside6/tests/pysidetest/repr_test.py
sources/pyside6/tests/pysidetest/shared_pointer_test.py
sources/pyside6/tests/pysidetest/signal_slot_warning.py
sources/pyside6/tests/pysidetest/signal_tp_descr_get_test.py
sources/pyside6/tests/pysidetest/signalandnamespace_test.py
sources/pyside6/tests/pysidetest/signalemissionfrompython_test.py
sources/pyside6/tests/pysidetest/signalinstance_equality_test.py
sources/pyside6/tests/pysidetest/signalwithdefaultvalue_test.py
sources/pyside6/tests/pysidetest/snake_case_sub.py
sources/pyside6/tests/pysidetest/snake_case_test.py
sources/pyside6/tests/pysidetest/testobject.cpp
sources/pyside6/tests/pysidetest/testobject.h
sources/pyside6/tests/pysidetest/testview.h
sources/pyside6/tests/pysidetest/true_property_test.py
sources/pyside6/tests/pysidetest/typedef_signal_test.py
sources/pyside6/tests/pysidetest/typesystem_pysidetest.xml
sources/pyside6/tests/pysidetest/version_test.py
sources/pyside6/tests/registry/existence_test.py
sources/pyside6/tests/registry/init_platform.py
sources/pyside6/tests/registry/scrape_testresults.py
sources/pyside6/tests/registry/util.py
sources/pyside6/tests/signals/anonymous_slot_leak_test.py
sources/pyside6/tests/signals/args_dont_match_test.py
sources/pyside6/tests/signals/bug_189.py
sources/pyside6/tests/signals/bug_311.py
sources/pyside6/tests/signals/bug_312.py
sources/pyside6/tests/signals/bug_319.py
sources/pyside6/tests/signals/bug_79.py
sources/pyside6/tests/signals/decorators_test.py
sources/pyside6/tests/signals/disconnect_test.py
sources/pyside6/tests/signals/invalid_callback_test.py
sources/pyside6/tests/signals/lambda_gui_test.py
sources/pyside6/tests/signals/lambda_test.py
sources/pyside6/tests/signals/leaking_signal_test.py
sources/pyside6/tests/signals/multiple_connections_gui_test.py
sources/pyside6/tests/signals/multiple_connections_test.py
sources/pyside6/tests/signals/pysignal_test.py
sources/pyside6/tests/signals/qobject_callable_connect_test.py
sources/pyside6/tests/signals/qobject_destroyed_test.py
sources/pyside6/tests/signals/qobject_receivers_test.py
sources/pyside6/tests/signals/qobject_sender_test.py
sources/pyside6/tests/signals/ref01_test.py
sources/pyside6/tests/signals/ref02_test.py
sources/pyside6/tests/signals/ref03_test.py
sources/pyside6/tests/signals/ref04_test.py
sources/pyside6/tests/signals/ref05_test.py
sources/pyside6/tests/signals/ref06_test.py
sources/pyside6/tests/signals/segfault_proxyparent_test.py
sources/pyside6/tests/signals/self_connect_test.py
sources/pyside6/tests/signals/short_circuit_test.py
sources/pyside6/tests/signals/signal2signal_connect_test.py
sources/pyside6/tests/signals/signal_across_threads.py
sources/pyside6/tests/signals/signal_autoconnect_test.py
sources/pyside6/tests/signals/signal_connectiontype_support_test.py
sources/pyside6/tests/signals/signal_emission_gui_test.py
sources/pyside6/tests/signals/signal_emission_test.py
sources/pyside6/tests/signals/signal_enum_test.py
sources/pyside6/tests/signals/signal_func_test.py
sources/pyside6/tests/signals/signal_manager_refcount_test.py
sources/pyside6/tests/signals/signal_newenum_test.py
sources/pyside6/tests/signals/signal_number_limit_test.py
sources/pyside6/tests/signals/signal_object_test.py
sources/pyside6/tests/signals/signal_signature_test.py
sources/pyside6/tests/signals/signal_with_primitive_type_test.py
sources/pyside6/tests/signals/slot_reference_count_test.py
sources/pyside6/tests/signals/static_metaobject_test.py
sources/pyside6/tests/support/voidptr_test.py
sources/pyside6/tests/tools/list-class-hierarchy.py
sources/pyside6/tests/tools/metaobjectdump/test_metaobjectdump.py
sources/pyside6/tests/tools/pyside6-android-deploy/extensive_android_deploy_test.py
sources/pyside6/tests/tools/pyside6-android-deploy/test_pyside6_android_deploy.py
sources/pyside6/tests/tools/pyside6-deploy/extensive_deploy_test.py
sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
sources/pyside6/tests/tools/pyside6-project/CMakeLists.txt [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad.qmlproject [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad.qrc [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad/TestComponent.qml [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad/qmldir [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/App.qml [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/MainScreen.ui.qml [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/qmldir [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/.pyproject [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/autogen/settings.py [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/main.py [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Resources/resource1.txt [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/Resources/resource2.txt [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/example_drumpad/qtquickcontrols2.conf [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py [new file with mode: 0644]
sources/pyside6/tests/tools/pyside6-qml/test_pyside6_qml.py
sources/pyside6/tests/util/color.py
sources/pyside6/tests/util/helper/basicpyslotcase.py
sources/pyside6/tests/util/helper/docmodifier.py
sources/pyside6/tests/util/helper/helper.py
sources/pyside6/tests/util/helper/timedqapplication.py
sources/pyside6/tests/util/helper/timedqguiapplication.py
sources/pyside6/tests/util/helper/usesqapplication.py
sources/pyside6/tests/util/httpd.py
sources/pyside6/tests/util/processtimer.py
sources/pyside6/tests/util/test_processtimer.py
sources/pyside6/tests/util/valgrind-python.supp
sources/shiboken6/.cmake.conf
sources/shiboken6/AUTHORS
sources/shiboken6/ApiExtractor/AUTHORS
sources/shiboken6/ApiExtractor/CMakeLists.txt
sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
sources/shiboken6/ApiExtractor/abstractmetabuilder_helpers.cpp
sources/shiboken6/ApiExtractor/abstractmetabuilder_p.h
sources/shiboken6/ApiExtractor/abstractmetaenum.cpp
sources/shiboken6/ApiExtractor/abstractmetafield.cpp
sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
sources/shiboken6/ApiExtractor/abstractmetafunction.h
sources/shiboken6/ApiExtractor/abstractmetalang.cpp
sources/shiboken6/ApiExtractor/abstractmetalang.h
sources/shiboken6/ApiExtractor/abstractmetalang_typedefs.h
sources/shiboken6/ApiExtractor/abstractmetatype.cpp
sources/shiboken6/ApiExtractor/addedfunction.cpp
sources/shiboken6/ApiExtractor/anystringview_helpers.cpp
sources/shiboken6/ApiExtractor/anystringview_helpers.h
sources/shiboken6/ApiExtractor/apiextractor.cpp
sources/shiboken6/ApiExtractor/apiextractor.h
sources/shiboken6/ApiExtractor/apiextractorresult.h
sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp
sources/shiboken6/ApiExtractor/clangparser/clangbuilder.h
sources/shiboken6/ApiExtractor/clangparser/clangdebugutils.cpp
sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp
sources/shiboken6/ApiExtractor/clangparser/clangparser.h
sources/shiboken6/ApiExtractor/clangparser/clangutils.cpp
sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp
sources/shiboken6/ApiExtractor/clangparser/compilersupport.h
sources/shiboken6/ApiExtractor/classdocumentation.cpp
sources/shiboken6/ApiExtractor/classdocumentation.h
sources/shiboken6/ApiExtractor/complextypeentry.h
sources/shiboken6/ApiExtractor/conditionalstreamreader.cpp
sources/shiboken6/ApiExtractor/docparser.h
sources/shiboken6/ApiExtractor/documentation.cpp
sources/shiboken6/ApiExtractor/documentation.h
sources/shiboken6/ApiExtractor/documentation_enums.h [new file with mode: 0644]
sources/shiboken6/ApiExtractor/doxygenparser.cpp
sources/shiboken6/ApiExtractor/doxygenparser.h
sources/shiboken6/ApiExtractor/enclosingclassmixin.h
sources/shiboken6/ApiExtractor/graph.h
sources/shiboken6/ApiExtractor/messages.cpp
sources/shiboken6/ApiExtractor/messages.h
sources/shiboken6/ApiExtractor/modifications.h
sources/shiboken6/ApiExtractor/parser/codemodel.cpp
sources/shiboken6/ApiExtractor/parser/codemodel.h
sources/shiboken6/ApiExtractor/parser/enumvalue.h
sources/shiboken6/ApiExtractor/parser/typeinfo.cpp
sources/shiboken6/ApiExtractor/parser/typeinfo.h
sources/shiboken6/ApiExtractor/predefined_templates.cpp
sources/shiboken6/ApiExtractor/propertyspec.cpp
sources/shiboken6/ApiExtractor/qtdocparser.cpp
sources/shiboken6/ApiExtractor/qtdocparser.h
sources/shiboken6/ApiExtractor/reporthandler.cpp
sources/shiboken6/ApiExtractor/tests/CMakeLists.txt
sources/shiboken6/ApiExtractor/tests/testcontainer.cpp
sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp
sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp
sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp
sources/shiboken6/ApiExtractor/tests/testnamespace.cpp
sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp
sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp
sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp
sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp
sources/shiboken6/ApiExtractor/tests/testremovefield.cpp
sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp
sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp
sources/shiboken6/ApiExtractor/tests/testtemplates.cpp
sources/shiboken6/ApiExtractor/tests/testtoposort.cpp
sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp
sources/shiboken6/ApiExtractor/textstream.cpp
sources/shiboken6/ApiExtractor/typedatabase.cpp
sources/shiboken6/ApiExtractor/typedatabase.h
sources/shiboken6/ApiExtractor/typeparser.cpp
sources/shiboken6/ApiExtractor/typesystem.cpp
sources/shiboken6/ApiExtractor/typesystem_enums.h
sources/shiboken6/ApiExtractor/typesystemparser.cpp
sources/shiboken6/ApiExtractor/typesystemtypeentry.h
sources/shiboken6/ApiExtractor/xmlutils.cpp
sources/shiboken6/CMakeLists.txt
sources/shiboken6/COPYING.libsample
sources/shiboken6/COPYING.libshiboken
sources/shiboken6/cmake/ShibokenHelpers.cmake
sources/shiboken6/config.tests/target_python_info/CMakeLists.txt
sources/shiboken6/config.tests/target_qt_info/CMakeLists.txt
sources/shiboken6/data/CMakeLists.txt
sources/shiboken6/data/docgenerator.1
sources/shiboken6/data/generatorrunner.1
sources/shiboken6/data/generatorrunner.pc.in
sources/shiboken6/doc/_static/css/qt_style.css
sources/shiboken6/doc/examples/index.rst
sources/shiboken6/doc/scripts/patch_qhp.py
sources/shiboken6/doc/shibokengenerator.rst
sources/shiboken6/doc/shibokenmodule.rst
sources/shiboken6/doc/typesystem.rst
sources/shiboken6/doc/typesystem_arguments.rst
sources/shiboken6/doc/typesystem_codegeneration.rst
sources/shiboken6/doc/typesystem_containers.rst
sources/shiboken6/doc/typesystem_conversionrule.rst
sources/shiboken6/doc/typesystem_documentation.rst
sources/shiboken6/doc/typesystem_manipulating_objects.rst
sources/shiboken6/doc/typesystem_modify_function.rst [deleted file]
sources/shiboken6/doc/typesystem_solving_compilation.rst
sources/shiboken6/doc/typesystem_specifying_types.rst
sources/shiboken6/doc/typesystem_templates.rst
sources/shiboken6/doc/typesystem_variables.rst
sources/shiboken6/generator/generator.cpp
sources/shiboken6/generator/generator.h
sources/shiboken6/generator/generatorcontext.cpp
sources/shiboken6/generator/generatorcontext.h
sources/shiboken6/generator/main.cpp
sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
sources/shiboken6/generator/qtdoc/qtdocgenerator.h
sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp
sources/shiboken6/generator/qtdoc/qtxmltosphinx.h
sources/shiboken6/generator/qtdoc/qtxmltosphinxinterface.h
sources/shiboken6/generator/shiboken/cppgenerator.cpp
sources/shiboken6/generator/shiboken/cppgenerator.h
sources/shiboken6/generator/shiboken/cppgenerator_container.cpp
sources/shiboken6/generator/shiboken/cppgenerator_smartpointer.cpp
sources/shiboken6/generator/shiboken/headergenerator.cpp
sources/shiboken6/generator/shiboken/headergenerator.h
sources/shiboken6/generator/shiboken/overloaddata.cpp
sources/shiboken6/generator/shiboken/overloaddata.h
sources/shiboken6/generator/shiboken/shibokengenerator.cpp
sources/shiboken6/generator/shiboken/shibokengenerator.h
sources/shiboken6/libshiboken/CMakeLists.txt
sources/shiboken6/libshiboken/basewrapper.cpp
sources/shiboken6/libshiboken/basewrapper.h
sources/shiboken6/libshiboken/basewrapper_p.h
sources/shiboken6/libshiboken/bindingmanager.cpp
sources/shiboken6/libshiboken/bindingmanager.h
sources/shiboken6/libshiboken/embed/embedding_generator.py
sources/shiboken6/libshiboken/embed/module_collector.py
sources/shiboken6/libshiboken/embed/signature_bootstrap.py
sources/shiboken6/libshiboken/gilstate.cpp
sources/shiboken6/libshiboken/gilstate.h
sources/shiboken6/libshiboken/helper.cpp
sources/shiboken6/libshiboken/pep384ext.h
sources/shiboken6/libshiboken/pep384impl.cpp
sources/shiboken6/libshiboken/pep384impl.h
sources/shiboken6/libshiboken/sbkarrayconverter.cpp
sources/shiboken6/libshiboken/sbkarrayconverter.h
sources/shiboken6/libshiboken/sbkconverter.cpp
sources/shiboken6/libshiboken/sbkconverter.h
sources/shiboken6/libshiboken/sbkconverter_p.h
sources/shiboken6/libshiboken/sbkcppstring.cpp
sources/shiboken6/libshiboken/sbkenum.cpp
sources/shiboken6/libshiboken/sbkenum.h
sources/shiboken6/libshiboken/sbkerrors.cpp
sources/shiboken6/libshiboken/sbkerrors.h
sources/shiboken6/libshiboken/sbkfeature_base.cpp
sources/shiboken6/libshiboken/sbkmodule.cpp
sources/shiboken6/libshiboken/sbkmodule.h
sources/shiboken6/libshiboken/sbkpython.h
sources/shiboken6/libshiboken/sbkstring.cpp
sources/shiboken6/libshiboken/sbkstring.h
sources/shiboken6/libshiboken/sbktypefactory.cpp
sources/shiboken6/libshiboken/shiboken.h
sources/shiboken6/libshiboken/shibokenbuffer.h
sources/shiboken6/libshiboken/signature.h
sources/shiboken6/libshiboken/signature/signature.cpp
sources/shiboken6/libshiboken/signature/signature_extend.cpp
sources/shiboken6/libshiboken/signature/signature_globals.cpp
sources/shiboken6/libshiboken/signature/signature_helper.cpp
sources/shiboken6/libshiboken/signature_p.h
sources/shiboken6/libshiboken/threadstatesaver.cpp
sources/shiboken6/libshiboken/threadstatesaver.h
sources/shiboken6/libshiboken/voidptr.cpp
sources/shiboken6/shiboken_tool.py
sources/shiboken6/shiboken_version.py
sources/shiboken6/shibokenmodule/Shiboken.pyi
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/__init__.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/fix-complaints.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/__init__.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/__init__.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/loader.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/mapping.py
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py
sources/shiboken6/shibokenmodule/shibokenmodule.cpp
sources/shiboken6/shibokenmodule/typesystem_shiboken.xml
sources/shiboken6/tests/libminimal/CMakeLists.txt
sources/shiboken6/tests/libminimal/listuser.h
sources/shiboken6/tests/libminimal/obj.cpp
sources/shiboken6/tests/libminimal/obj.h
sources/shiboken6/tests/libminimal/val.h
sources/shiboken6/tests/libother/CMakeLists.txt
sources/shiboken6/tests/libother/othermultiplederived.h
sources/shiboken6/tests/libsample/CMakeLists.txt
sources/shiboken6/tests/libsample/collector.h
sources/shiboken6/tests/libsample/complex.h
sources/shiboken6/tests/libsample/derived.h
sources/shiboken6/tests/libsample/filter.h
sources/shiboken6/tests/libsample/injectcode.h
sources/shiboken6/tests/libsample/listuser.h
sources/shiboken6/tests/libsample/main.cpp
sources/shiboken6/tests/libsample/modified_constructor.cpp
sources/shiboken6/tests/libsample/modified_constructor.h
sources/shiboken6/tests/libsample/noimplicitconversion.h
sources/shiboken6/tests/libsmart/CMakeLists.txt
sources/shiboken6/tests/libsmart/stdsharedptrtestbench.cpp
sources/shiboken6/tests/libsmart/stdsharedptrtestbench.h
sources/shiboken6/tests/minimalbinding/CMakeLists.txt
sources/shiboken6/tests/minimalbinding/brace_pattern_test.py
sources/shiboken6/tests/minimalbinding/containeruser_test.py
sources/shiboken6/tests/minimalbinding/listuser_test.py
sources/shiboken6/tests/minimalbinding/minbool_test.py
sources/shiboken6/tests/minimalbinding/obj_test.py
sources/shiboken6/tests/minimalbinding/spanuser_test.py
sources/shiboken6/tests/minimalbinding/typedef_test.py
sources/shiboken6/tests/minimalbinding/val_test.py
sources/shiboken6/tests/otherbinding/CMakeLists.txt
sources/shiboken6/tests/otherbinding/collector_external_operator_test.py
sources/shiboken6/tests/otherbinding/conversion_operator_for_class_without_implicit_conversions_test.py
sources/shiboken6/tests/otherbinding/extended_multiply_operator_test.py
sources/shiboken6/tests/otherbinding/module_reload_test.py
sources/shiboken6/tests/otherbinding/new_ctor_operator_test.py
sources/shiboken6/tests/otherbinding/objtypehashes_test.py
sources/shiboken6/tests/otherbinding/otherderived_test.py
sources/shiboken6/tests/otherbinding/othertypesystypedef_test.py
sources/shiboken6/tests/otherbinding/signature_test.py
sources/shiboken6/tests/otherbinding/smartptr_test.py
sources/shiboken6/tests/otherbinding/star_import_test.py
sources/shiboken6/tests/otherbinding/test_module_template.py
sources/shiboken6/tests/otherbinding/typediscovery_test.py
sources/shiboken6/tests/otherbinding/usersprimitivefromothermodule_test.py
sources/shiboken6/tests/otherbinding/wrongctor_test.py
sources/shiboken6/tests/qtxmltosphinx/main.cpp
sources/shiboken6/tests/qtxmltosphinxtest/qtxmltosphinxtest.cpp
sources/shiboken6/tests/samplebinding/CMakeLists.txt
sources/shiboken6/tests/samplebinding/__del___test.py
sources/shiboken6/tests/samplebinding/abstract_test.py
sources/shiboken6/tests/samplebinding/addedfunction_test.py
sources/shiboken6/tests/samplebinding/addedfunction_with_container_args_test.py
sources/shiboken6/tests/samplebinding/argumentmodifications_test.py
sources/shiboken6/tests/samplebinding/array_numpy_test.py
sources/shiboken6/tests/samplebinding/array_sequence_test.py
sources/shiboken6/tests/samplebinding/bug_554_test.py
sources/shiboken6/tests/samplebinding/bug_704_test.py
sources/shiboken6/tests/samplebinding/bytearray_test.py
sources/shiboken6/tests/samplebinding/child_return_test.py
sources/shiboken6/tests/samplebinding/class_fields_test.py
sources/shiboken6/tests/samplebinding/collector_test.py
sources/shiboken6/tests/samplebinding/complex_test.py
sources/shiboken6/tests/samplebinding/conversion_operator_test.py
sources/shiboken6/tests/samplebinding/copy_test.py
sources/shiboken6/tests/samplebinding/ctorconvrule_test.py
sources/shiboken6/tests/samplebinding/cyclic_test.py
sources/shiboken6/tests/samplebinding/date_test.py
sources/shiboken6/tests/samplebinding/decisor_test.py
sources/shiboken6/tests/samplebinding/delete_test.py
sources/shiboken6/tests/samplebinding/deprecated_test.py
sources/shiboken6/tests/samplebinding/derived_test.py
sources/shiboken6/tests/samplebinding/duck_punching_test.py
sources/shiboken6/tests/samplebinding/echo_test.py
sources/shiboken6/tests/samplebinding/enum_test.py
sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py
sources/shiboken6/tests/samplebinding/event_loop_call_virtual_test.py
sources/shiboken6/tests/samplebinding/event_loop_thread_test.py
sources/shiboken6/tests/samplebinding/exception_test.py
sources/shiboken6/tests/samplebinding/filter_test.py
sources/shiboken6/tests/samplebinding/handleholder_test.py
sources/shiboken6/tests/samplebinding/hashabletype_test.py
sources/shiboken6/tests/samplebinding/ignorederefop_test.py
sources/shiboken6/tests/samplebinding/implicitconv_numerical_test.py
sources/shiboken6/tests/samplebinding/implicitconv_test.py
sources/shiboken6/tests/samplebinding/inheritanceandscope_test.py
sources/shiboken6/tests/samplebinding/injectcode_test.py
sources/shiboken6/tests/samplebinding/innerclass_test.py
sources/shiboken6/tests/samplebinding/intlist_test.py
sources/shiboken6/tests/samplebinding/intwrapper_test.py
sources/shiboken6/tests/samplebinding/invalid_virtual_return_test.py
sources/shiboken6/tests/samplebinding/keep_reference_test.py
sources/shiboken6/tests/samplebinding/list_test.py
sources/shiboken6/tests/samplebinding/lock_test.py
sources/shiboken6/tests/samplebinding/map_test.py
sources/shiboken6/tests/samplebinding/metaclass_test.py
sources/shiboken6/tests/samplebinding/mi_virtual_methods_test.py
sources/shiboken6/tests/samplebinding/mixed_mi_test.py
sources/shiboken6/tests/samplebinding/modelindex_test.py
sources/shiboken6/tests/samplebinding/modelview_test.py
sources/shiboken6/tests/samplebinding/modifications_test.py
sources/shiboken6/tests/samplebinding/modified_constructor_test.py
sources/shiboken6/tests/samplebinding/modifiedvirtualmethods_test.py
sources/shiboken6/tests/samplebinding/multi_cpp_inheritance_test.py
sources/shiboken6/tests/samplebinding/multiple_derived_test.py
sources/shiboken6/tests/samplebinding/namespace_test.py
sources/shiboken6/tests/samplebinding/newdivision_test.py
sources/shiboken6/tests/samplebinding/nondefaultctor_test.py
sources/shiboken6/tests/samplebinding/nontypetemplate_test.py
sources/shiboken6/tests/samplebinding/nonzero_test.py
sources/shiboken6/tests/samplebinding/numericaltypedef_test.py
sources/shiboken6/tests/samplebinding/numpy_test.py
sources/shiboken6/tests/samplebinding/objecttype_test.py
sources/shiboken6/tests/samplebinding/objecttype_with_named_args_test.py
sources/shiboken6/tests/samplebinding/objecttypebyvalue_test.py
sources/shiboken6/tests/samplebinding/objecttypelayout_test.py
sources/shiboken6/tests/samplebinding/objecttypeoperators_test.py
sources/shiboken6/tests/samplebinding/objecttypereferenceasvirtualmethodargument_test.py
sources/shiboken6/tests/samplebinding/oddbool_test.py
sources/shiboken6/tests/samplebinding/onlycopyclass_test.py
sources/shiboken6/tests/samplebinding/overflow_test.py
sources/shiboken6/tests/samplebinding/overload_sorting_test.py
sources/shiboken6/tests/samplebinding/overload_test.py
sources/shiboken6/tests/samplebinding/overloadwithdefault_test.py
sources/shiboken6/tests/samplebinding/ownership_argument_invalidation_test.py
sources/shiboken6/tests/samplebinding/ownership_delete_child_in_cpp_test.py
sources/shiboken6/tests/samplebinding/ownership_delete_child_in_python_test.py
sources/shiboken6/tests/samplebinding/ownership_delete_parent_test.py
sources/shiboken6/tests/samplebinding/ownership_invalidate_after_use_test.py
sources/shiboken6/tests/samplebinding/ownership_invalidate_child_test.py
sources/shiboken6/tests/samplebinding/ownership_invalidate_nonpolymorphic_test.py
sources/shiboken6/tests/samplebinding/ownership_invalidate_parent_test.py
sources/shiboken6/tests/samplebinding/ownership_reparenting_test.py
sources/shiboken6/tests/samplebinding/ownership_transference_test.py
sources/shiboken6/tests/samplebinding/pair_test.py
sources/shiboken6/tests/samplebinding/pen_test.py
sources/shiboken6/tests/samplebinding/point_test.py
sources/shiboken6/tests/samplebinding/pointerholder_test.py
sources/shiboken6/tests/samplebinding/pointerprimitivetype_test.py
sources/shiboken6/tests/samplebinding/pointf_test.py
sources/shiboken6/tests/samplebinding/primitivereferenceargument_test.py
sources/shiboken6/tests/samplebinding/privatector_test.py
sources/shiboken6/tests/samplebinding/privatedtor_test.py
sources/shiboken6/tests/samplebinding/protected_test.py
sources/shiboken6/tests/samplebinding/pstrlist_test.py
sources/shiboken6/tests/samplebinding/pystr_test.py
sources/shiboken6/tests/samplebinding/python_thread_test.py
sources/shiboken6/tests/samplebinding/receive_null_cstring_test.py
sources/shiboken6/tests/samplebinding/reference_test.py
sources/shiboken6/tests/samplebinding/referencetopointer_test.py
sources/shiboken6/tests/samplebinding/renaming_test.py
sources/shiboken6/tests/samplebinding/return_null_test.py
sources/shiboken6/tests/samplebinding/richcompare_test.py
sources/shiboken6/tests/samplebinding/sample_test.py
sources/shiboken6/tests/samplebinding/simplefile_test.py
sources/shiboken6/tests/samplebinding/size_test.py
sources/shiboken6/tests/samplebinding/snakecase_test.py
sources/shiboken6/tests/samplebinding/static_nonstatic_methods_test.py
sources/shiboken6/tests/samplebinding/stdcomplex_test.py
sources/shiboken6/tests/samplebinding/str_test.py
sources/shiboken6/tests/samplebinding/strlist_test.py
sources/shiboken6/tests/samplebinding/templateinheritingclass_test.py
sources/shiboken6/tests/samplebinding/time_test.py
sources/shiboken6/tests/samplebinding/transform_test.py
sources/shiboken6/tests/samplebinding/typeconverters_test.py
sources/shiboken6/tests/samplebinding/typedealloc_test.py
sources/shiboken6/tests/samplebinding/typedtordoublefree_test.py
sources/shiboken6/tests/samplebinding/typesystem_sample.xml
sources/shiboken6/tests/samplebinding/typesystypedef_test.py
sources/shiboken6/tests/samplebinding/unsafe_parent_test.py
sources/shiboken6/tests/samplebinding/useraddedctor_test.py
sources/shiboken6/tests/samplebinding/virtualdtor_test.py
sources/shiboken6/tests/samplebinding/virtualmethods_test.py
sources/shiboken6/tests/samplebinding/visibilitychange_test.py
sources/shiboken6/tests/samplebinding/voidholder_test.py
sources/shiboken6/tests/samplebinding/weakref_test.py
sources/shiboken6/tests/samplebinding/writableclassdict_test.py
sources/shiboken6/tests/shiboken_paths.py
sources/shiboken6/tests/shiboken_test_helper.py
sources/shiboken6/tests/shibokenmodule/module_test.py
sources/shiboken6/tests/smartbinding/CMakeLists.txt
sources/shiboken6/tests/smartbinding/smart_pointer_test.py
sources/shiboken6/tests/smartbinding/std_optional_test.py
sources/shiboken6/tests/smartbinding/std_shared_ptr_test.py
sources/shiboken6/tests/smartbinding/std_unique_ptr_test.py
sources/shiboken6/tests/smartbinding/typesystem_smart.xml
sources/shiboken6/tests/test_generator/CMakeLists.txt
sources/shiboken6/tests/test_generator/dummygenerator.cpp
sources/shiboken6/tests/test_generator/dummygentest-project.txt.in
sources/shiboken6/tests/test_generator/dummygentest.cpp
sources/shiboken6/tests/test_generator/dummygentest.h
sources/shiboken6/tests/test_generator/dummygentestconfig.h.in
sources/shiboken6/tests/test_generator/main.cpp
testing/__init__.py
testing/blacklist.py
testing/buildlog.py
testing/command.py
testing/helper.py
testing/parser.py
testing/runner.py
testing/wheel_tester.py
testrunner.py
tools/broken_links.py [new file with mode: 0644]
tools/checklibs.py
tools/create_changelog.py
tools/cross_compile_android/android_utilities.py
tools/cross_compile_android/main.py
tools/cross_compile_android/requirements.txt
tools/cross_compile_android/templates/cross_compile.tmpl.sh
tools/cross_compile_android/templates/toolchain_default.tmpl.cmake
tools/debug_renamer.py
tools/debug_windows.py
tools/doc_modules.py
tools/dump_metaobject.py
tools/example_gallery/main.py
tools/install-p311.sh [new file with mode: 0755]
tools/leak_finder.py
tools/license_check.py
tools/metaobject_dump.py
tools/missing_bindings/config.py
tools/missing_bindings/main.py
tools/qtcpp2py.py
tools/regenerate_example_resources.py
tools/regenerate_example_ui.py
tools/release_notes/main.py [new file with mode: 0644]
tools/scanqtclasses.py [new file with mode: 0644]
tools/snippets_translate/converter.py
tools/snippets_translate/handlers.py
tools/snippets_translate/main.py
tools/snippets_translate/module_classes.py
tools/snippets_translate/override.py
tools/snippets_translate/parse_utils.py
tools/snippets_translate/tests/test_converter.py
tools/snippets_translate/tests/test_snippets.py
tools/uic_test.py
wheel_artifacts/pyproject.toml.base

index 27fd3efc2f99866ef664ae71b548a81b129e6b61..e898e69934bbe0bd4fb7ff4aa330b80d4d3b2ccc 100644 (file)
@@ -12,13 +12,15 @@ sources/pyside6/doc/codesnippets/*
 # Auto-generated examples
 sources/pyside6/doc/examples/*
 
+# Auto-generated release notes
+sources/pyside6/doc/release_notes/*
+
 distribute-*.egg
 distribute-*.tar.gz
 explore2
 build_history/2*
 
 *.qdocconf
-*.qdocconf.in
 
 *.egg-info
 *.pyc
index 3d19a40b45948bc2f670fd2676d12a09405992b0..51dbfdb20253b5bdf2751c88245653d80fcd9e4e 100644 (file)
@@ -15,5 +15,6 @@
     "python.testing.unittestEnabled": true,
     "flake8.args": [
         "--config=.flake8"
-    ]
+    ],
+    "grammarly.files.include": ["**/*.rst", "**/*.md"],
 }
index 35f5a9063140a260c52de9df9272d9e0ef59a11b..bb98271d3554ba32f8c3951dce384a8b5216a1a2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@ using `setup.py build`:
  * `--ignore-git`, will skip the fetching and checkout steps for supermodule
    and all submodules.
  * `--limited-api=yes|no`, default yes if applicable. Set or clear the limited
-   API flag. Ignored for Python 2.
+   API flag.
  * `--module-subset`, allows for specifying the Qt modules to be built.
    A minimal set is: `--module-subset=Core,Gui,Test,Widgets`.
  * `--package-timestamp`, allows specifying the timestamp that will be used as
index e6044c4a4c86b0074481e3aef611db315c7cea67..a42576706be5c6e0c5bea53247537089adc42f52 100644 (file)
@@ -23,6 +23,7 @@ it includes the following Qt modules:
 * QtCharts
 * QtDataVisualization
 * QtGraphs
+* QtGraphsWidgets
 * QtMultimedia
 * QtMultimediaWidgets
 * QtNetworkAuth
@@ -48,6 +49,7 @@ it includes the following Qt modules:
 * QtHttpServer
 * QtLocation
 * QtAsyncio
+* QtWebView
 
 ### Documentation and Bugs
 
index 128bb2394c29fcf6013697519ba08edf36b5df2a..17975e34d06554aca89ddfbb5fd4d835999a32d9 100644 (file)
@@ -1,5 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+from pathlib import Path
 
 PYSIDE = 'pyside6'
 PYSIDE_MODULE = 'PySide6'
@@ -20,7 +22,8 @@ PYSIDE_UNIX_BIN_TOOLS = ["lupdate",
                          "qmlls",
                          "qsb",
                          "balsam",
-                         "balsamui"]
+                         "balsamui",
+                         "svgtoqml",]
 
 # tools that are bundled as .app in macOS, but are normal executables in Linux and Windows
 PYSIDE_UNIX_BUNDLED_TOOLS = ["assistant",
@@ -37,3 +40,8 @@ PYSIDE_UNIX_LIBEXEC_TOOLS = ["uic",
 
 # all Qt tools are in 'bin' folder in Windows
 PYSIDE_WINDOWS_BIN_TOOLS = PYSIDE_UNIX_LIBEXEC_TOOLS + PYSIDE_LINUX_BIN_TOOLS
+
+PYSIDE_MULTIMEDIA_LIBS = ["avcodec", "avformat", "avutil",
+                          "swresample", "swscale"]
+
+PYPROJECT_PATH = Path(__file__).parents[1] / "wheel_artifacts" / "pyproject.toml.base"
index 30ce187c817cfe09682c2572cc32f1dde407b510..0c8becf49dc5f966a95681c490f3053eb01245d6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2021 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import platform
@@ -129,7 +130,7 @@ def get_py_library(build_type, py_version, py_prefix, py_libdir, py_include_dir)
     return py_library
 
 
-class BuildInfoCollectorMixin(object):
+class BuildInfoCollectorMixin:
     build_base: str
     build_lib: str
     cmake: str
index 0a6eebf7801cb6ddfdc4cdf2abaefa63f9097bf7..f11fbb5e789247290a4d52faa2e13ccb9b9af51a 100644 (file)
@@ -1,15 +1,23 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
+import os
 import sys
-from .log import log, LogLevel
 from pathlib import Path
+from typing import Any
 
-from . import PYSIDE, PYSIDE_MODULE, SHIBOKEN
-from .utils import available_pyside_tools
+from . import PYPROJECT_PATH, PYSIDE, PYSIDE_MODULE, SHIBOKEN
+from .log import LogLevel, log
+from .utils import available_pyside_tools, Singleton
 
+try:
+    import tomllib
+except ModuleNotFoundError:
+    import tomli as tomllib
 
-class Config(object):
+
+class Config(metaclass=Singleton):
     def __init__(self):
         # Constants
         self._build_type_all = "all"
@@ -22,7 +30,7 @@ class Config(object):
         # The setup.py invocation type.
         # top-level
         # internal
-        self.invocation_type = None
+        self.invocation_type: str = ""
 
         # The type of the top-level build.
         # all - build shiboken6 module, shiboken6-generator and PySide6
@@ -30,11 +38,11 @@ class Config(object):
         # shiboken6 - build only shiboken6 module
         # shiboken6-generator - build only the shiboken6-generator
         # pyside6 - build only PySide6 modules
-        self.build_type = None
+        self.build_type: str = ""
 
         # The internal build type, used for internal invocations of
         # setup.py to build a specific module only.
-        self.internal_build_type = None
+        self.internal_build_type: str = ""
 
         # Options that can be given to --build-type and
         # --internal-build-type
@@ -45,40 +53,51 @@ class Config(object):
         # Names to be passed to setuptools.setup() name key,
         # so not package name, but rather project name as it appears
         # in the wheel name and on PyPi.
-        self.shiboken_module_st_name = SHIBOKEN
-        self.shiboken_generator_st_name = f"{SHIBOKEN}-generator"
-        self.pyside_st_name = PYSIDE_MODULE
+        self.shiboken_module_st_name: str = SHIBOKEN
+        self.shiboken_generator_st_name: str = f"{SHIBOKEN}-generator"
+        self.pyside_st_name: str = PYSIDE_MODULE
 
         # Path to CMake toolchain file when intending to cross compile
         # the project.
-        self.cmake_toolchain_file = None
+        self.cmake_toolchain_file: str | os.PathLike = ""
 
         # Store where host shiboken is built during a cross-build.
-        self.shiboken_host_query_path = None
-
-        # Used by check_allowed_python_version to validate the
-        # interpreter version.
-        self.python_version_classifiers = [
-            'Programming Language :: Python',
-            'Programming Language :: Python :: 3',
-            'Programming Language :: Python :: 3.9',
-            'Programming Language :: Python :: 3.10',
-            'Programming Language :: Python :: 3.11',
-            'Programming Language :: Python :: 3.12',
-        ]
+        self.shiboken_host_query_path: str = ""
+
+        self.setup_script_dir: str | os.PathLike = ""
+
+        # Getting data from base pyproject.toml file to be consistent
+
+        if not PYPROJECT_PATH.exists():
+            log.error("'pyproject.toml.base' not found in '{pyproject_path}'")
 
-        self.setup_script_dir = None
+        with open(PYPROJECT_PATH, "rb") as f:
+            _pyproject_data = tomllib.load(f)["project"]
+
+        self.setup_kwargs: dict[str, Any] = {}
+        self.setup_kwargs['long_description_content_type'] = 'text/markdown'
+
+        self.setup_kwargs['keywords'] = _pyproject_data["keywords"]
+        _author, _email = _pyproject_data["authors"][0]
+        self.setup_kwargs['author'] = _author
+        self.setup_kwargs['author_email'] = _email
+        self.setup_kwargs['url'] = _pyproject_data["urls"]["Homepage"]
+        self.setup_kwargs['license'] = _pyproject_data["license"]["text"]
+        self.setup_kwargs['python_requires'] = _pyproject_data["requires-python"]
+
+        self.classifiers = _pyproject_data["classifiers"]
+        self.setup_kwargs['classifiers'] = self.classifiers
 
     def init_config(self,
-                    build_type=None,
-                    internal_build_type=None,
+                    build_type="",
+                    internal_build_type="",
                     cmd_class_dict=None,
                     package_version=None,
                     ext_modules=None,
-                    setup_script_dir=None,
-                    cmake_toolchain_file=None,
+                    setup_script_dir: str | os.PathLike = "",
+                    cmake_toolchain_file: str | os.PathLike = "",
                     log_level=LogLevel.INFO,
-                    qt_install_path: Path = None):
+                    qt_install_dir: str | os.PathLike = ""):
         """
         Sets up the global singleton config which is used in many parts
         of the setup process.
@@ -103,24 +122,14 @@ class Config(object):
 
         self.cmake_toolchain_file = cmake_toolchain_file
 
-        setup_kwargs = {}
-        setup_kwargs['long_description'] = self.get_long_description()
-        setup_kwargs['long_description_content_type'] = 'text/markdown'
-        setup_kwargs['keywords'] = 'Qt'
-        setup_kwargs['author'] = 'Qt for Python Team'
-        setup_kwargs['author_email'] = 'pyside@qt-project.org'
-        setup_kwargs['url'] = 'https://www.pyside.org'
-        setup_kwargs['download_url'] = 'https://download.qt.io/official_releases/QtForPython'
-        setup_kwargs['license'] = 'LGPL'
-        setup_kwargs['zip_safe'] = False
-        setup_kwargs['cmdclass'] = cmd_class_dict
-        setup_kwargs['version'] = package_version
-        setup_kwargs['python_requires'] = ">=3.9, <3.13"
+        self.setup_kwargs['long_description'] = self.get_long_description()
+        self.setup_kwargs['cmdclass'] = cmd_class_dict
+        self.setup_kwargs['version'] = package_version
 
         if log_level == LogLevel.QUIET:
             # Tells setuptools to be quiet, and only print warnings or errors.
             # Makes way less noise in the terminal when building.
-            setup_kwargs['verbose'] = 0
+            self.setup_kwargs['verbose'] = 0
 
         # Setting these two keys is still a bit of a discussion point.
         # In general not setting them will allow using "build" and
@@ -138,54 +147,29 @@ class Config(object):
         # The only plausible usage of it, is if we will implement a
         # correctly functioning setup.py develop command (or bdist_egg).
         # But currently that doesn't seem to work.
-        setup_kwargs['packages'] = self.get_setup_tools_packages_for_current_build()
-        setup_kwargs['package_dir'] = self.get_package_name_to_dir_path_mapping()
+        self.setup_kwargs['packages'] = self.get_setup_tools_packages_for_current_build()
+        self.setup_kwargs['package_dir'] = self.get_package_name_to_dir_path_mapping()
 
         # Add a bogus extension module (will never be built here since
         # we are overriding the build command to do it using cmake) so
         # things like bdist_egg will know that there are extension
         # modules and will name the dist with the full platform info.
-        setup_kwargs['ext_modules'] = ext_modules
-
-        common_classifiers = [
-            'Development Status :: 5 - Production/Stable',
-            'Environment :: Console',
-            'Environment :: MacOS X',
-            'Environment :: X11 Applications :: Qt',
-            'Environment :: Win32 (MS Windows)',
-            'Intended Audience :: Developers',
-            'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
-            'License :: Other/Proprietary License',
-            'Operating System :: MacOS :: MacOS X',
-            'Operating System :: POSIX',
-            'Operating System :: POSIX :: Linux',
-            'Operating System :: Microsoft',
-            'Operating System :: Microsoft :: Windows',
-            'Programming Language :: C++']
-        common_classifiers.extend(self.python_version_classifiers)
-        common_classifiers.extend([
-            'Topic :: Database',
-            'Topic :: Software Development',
-            'Topic :: Software Development :: Code Generators',
-            'Topic :: Software Development :: Libraries :: Application Frameworks',
-            'Topic :: Software Development :: User Interfaces',
-            'Topic :: Software Development :: Widget Sets'])
-        setup_kwargs['classifiers'] = common_classifiers
+        self.setup_kwargs['ext_modules'] = ext_modules
 
         package_name = self.package_name()
 
         if self.internal_build_type == self.shiboken_module_option_name:
-            setup_kwargs['name'] = self.shiboken_module_st_name
-            setup_kwargs['description'] = "Python / C++ bindings helper module"
-            setup_kwargs['entry_points'] = {}
+            self.setup_kwargs['name'] = self.shiboken_module_st_name
+            self.setup_kwargs['description'] = "Python / C++ bindings helper module"
+            self.setup_kwargs['entry_points'] = {}
 
         elif self.internal_build_type == self.shiboken_generator_option_name:
-            setup_kwargs['name'] = self.shiboken_generator_st_name
-            setup_kwargs['description'] = "Python / C++ bindings generator"
-            setup_kwargs['install_requires'] = [
+            self.setup_kwargs['name'] = self.shiboken_generator_st_name
+            self.setup_kwargs['description'] = "Python / C++ bindings generator"
+            self.setup_kwargs['install_requires'] = [
                 f"{self.shiboken_module_st_name}=={package_version}"
             ]
-            setup_kwargs['entry_points'] = {
+            self.setup_kwargs['entry_points'] = {
                 'console_scripts': [
                     f'{SHIBOKEN} = {package_name}.scripts.shiboken_tool:main',
                     f'{SHIBOKEN}-genpyi = {package_name}.scripts.shiboken_tool:genpyi',
@@ -193,19 +177,20 @@ class Config(object):
             }
 
         elif self.internal_build_type == self.pyside_option_name:
-            setup_kwargs['name'] = self.pyside_st_name
-            setup_kwargs['description'] = ("Python bindings for the Qt cross-platform application "
-                                           "and UI framework")
-            setup_kwargs['install_requires'] = [
+            self.setup_kwargs['name'] = self.pyside_st_name
+            self.setup_kwargs['description'] = (
+                "Python bindings for the Qt cross-platform application and UI framework"
+            )
+            self.setup_kwargs['install_requires'] = [
                 f"{self.shiboken_module_st_name}=={package_version}"
             ]
-            if qt_install_path:
-                _pyside_tools = available_pyside_tools(qt_tools_path=qt_install_path)
+            if qt_install_dir:
+                _pyside_tools = available_pyside_tools(qt_tools_path=Path(qt_install_dir))
 
                 # replacing pyside6-android_deploy by pyside6-android-deploy for consistency
-                # Also, the tool should not exist in any other platform than Linux
+                # Also, the tool should not exist in any other platform than Linux and macOS
                 _console_scripts = []
-                if ("android_deploy" in _pyside_tools) and sys.platform.startswith("linux"):
+                if ("android_deploy" in _pyside_tools) and sys.platform in ["linux", "darwin"]:
                     _console_scripts = [(f"{PYSIDE}-android-deploy ="
                                         " PySide6.scripts.pyside_tool:android_deploy")]
                 _pyside_tools.remove("android_deploy")
@@ -213,9 +198,7 @@ class Config(object):
                 _console_scripts.extend([f'{PYSIDE}-{tool} = {package_name}.scripts.pyside_tool:'
                                          f'{tool}' for tool in _pyside_tools])
 
-                setup_kwargs['entry_points'] = {'console_scripts': _console_scripts}
-
-        self.setup_kwargs = setup_kwargs
+                self.setup_kwargs['entry_points'] = {'console_scripts': _console_scripts}
 
     def get_long_description(self):
         readme_filename = 'README.md'
@@ -228,31 +211,23 @@ class Config(object):
         elif self.is_internal_pyside_build():
             readme_filename = f'README.{PYSIDE}.md'
 
-        content = ''
-        changes = ''
-        try:
-            with open(self.setup_script_dir / readme_filename) as f:
-                readme = f.read()
-        except Exception as e:
-            log.error(f"Couldn't read contents of {readme_filename}. {e}")
-            raise
+        with open(Path(self.setup_script_dir) / readme_filename) as f:
+            readme = f.read()
 
         # Don't include CHANGES.rst for now, because we have not decided
         # how to handle change files yet.
         include_changes = False
         if include_changes:
             try:
-                with open(self.setup_script_dir / changes_filename) as f:
+                changes = ''
+                with open(Path(self.setup_script_dir) / changes_filename) as f:
                     changes = f.read()
             except Exception as e:
                 log.error(f"Couldn't read contents of {changes_filename}. {e}")
                 raise
-        content += readme
-
-        if changes:
-            content += f"\n\n{changes}"
+            return f"{readme}\n\n{changes}"
 
-        return content
+        return readme
 
     def package_name(self):
         """
index c9ccf3fb9c883a8bc329f582cf9bcf0242a5e0b9..dfff76a06c63556378f44f8edffb76a48befcf88 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import logging
 
index bf71f9962240baa096041737896807a91938d792..62602ef4b195fb7ddb1e4e8c33f520414907b359 100644 (file)
@@ -1,17 +1,18 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import importlib
 import os
 import platform
 import re
 import sys
+import subprocess
 import sysconfig
 import time
 from packaging.version import parse as parse_version
 from pathlib import Path
 from shutil import copytree, rmtree
-from textwrap import dedent
 
 # PYSIDE-1760: Pre-load setuptools modules early to avoid racing conditions.
 #              may be touched (should be avoided anyway, btw.)
@@ -38,7 +39,7 @@ from .platforms.windows_desktop import prepare_packages_win32
 from .qtinfo import QtInfo
 from .utils import (copydir, copyfile, detect_clang,
                     get_numpy_location, get_python_dict,
-                    linux_fix_rpaths_for_library, macos_fix_rpaths_for_library,
+                    linux_fix_rpaths_for_library, macos_fix_rpaths_for_library, parse_modules,
                     platform_cmake_options, remove_tree, run_process,
                     run_process_output, update_env_path, which)
 from . import PYSIDE, PYSIDE_MODULE, SHIBOKEN
@@ -119,7 +120,7 @@ def get_allowed_python_versions():
     pattern = r'Programming Language :: Python :: (\d+)\.(\d+)'
     supported = []
 
-    for line in config.python_version_classifiers:
+    for line in config.classifiers:
         found = re.search(pattern, line)
         if found:
             major = int(found.group(1))
@@ -160,6 +161,22 @@ def prepare_build():
                     qt_src_dir = maybe_qt_src_dir
 
 
+def get_soname(clang_lib_path: Path) -> str:
+    """Getting SONAME from a shared library using readelf. Works only on Linux.
+    """
+    clang_lib_path = Path(clang_lib_path)
+    try:
+        result = subprocess.run(['readelf', '-d', str(clang_lib_path)],
+                                capture_output=True, text=True, check=True)
+        for line in result.stdout.split('\n'):
+            if 'SONAME' in line:
+                soname = line.split('[')[1].split(']')[0]
+                return soname
+    except subprocess.CalledProcessError as e:
+        print(f"Failed to get SONAME: {e}")
+    return None
+
+
 class PysideInstall(_install, CommandMixin):
 
     user_options = _install.user_options + CommandMixin.mixin_user_options
@@ -390,18 +407,18 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
             for ext in config.get_buildable_extensions():
                 self.build_extension(ext)
 
-            if OPTION["BUILDTESTS"]:
-                # we record the latest successful build and note the
-                # build directory for supporting the tests.
-                timestamp = time.strftime('%Y-%m-%d_%H%M%S')
-                build_history = setup_script_dir / 'build_history'
-                unique_dir = build_history / timestamp
-                unique_dir.mkdir(parents=True)
-                fpath = unique_dir / 'build_dir.txt'
-                with open(fpath, 'w') as f:
-                    print(self.build_dir, file=f)
-                    print(self.build_classifiers, file=f)
-                log.info(f"Created {build_history}")
+            # We always record the history, whether tests are built or not.
+            # Record the latest successful build and note the
+            # build directory for supporting the tests or other tools.
+            timestamp = time.strftime('%Y-%m-%d_%H%M%S')
+            build_history = setup_script_dir / 'build_history'
+            unique_dir = build_history / timestamp
+            unique_dir.mkdir(parents=True)
+            fpath = unique_dir / 'build_dir.txt'
+            with open(fpath, 'w') as f:
+                print(self.build_dir, file=f)
+                print(self.build_classifiers, file=f)
+            log.info(f"Created {build_history}")
 
         if not OPTION["SKIP_PACKAGING"]:
             # Build patchelf if needed
@@ -448,6 +465,7 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
                 except Exception as e:
                     log.warning(f'problem renaming "{self.st_build_dir}"')
                     log.warning(f'ignored error: {type(e).__name__}: {e}')
+
         else:
             log.info("Skipped preparing and building packages.")
         log.info(f"--- Build completed ({elapsed()}s)")
@@ -468,27 +486,13 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
         log.info(f"Make generator: {self.make_generator}")
         log.info(f"Make jobs:      {OPTION['JOBS']}")
         log.info("-" * 3)
-        log.info(f"setup.py directory:      {self.script_dir}")
-        log.info(f"Build scripts directory: {build_scripts_dir}")
-        log.info(f"Sources directory:       {self.sources_dir}")
-        log.info(dedent(f"""
-        Building {config.package_name()} will create and touch directories
-          in the following order:
-            make build directory ->
-            make install directory ->
-            setuptools build directory ->
-            setuptools install directory
-              (usually path-installed-python/lib/python*/site-packages/*)
-         """))
+        log.info(f"setup.py directory:           {self.script_dir}")
+        log.info(f"Build scripts directory:      {build_scripts_dir}")
+        log.info(f"Sources directory:            {self.sources_dir}")
         log.info(f"make build directory:         {self.build_dir}")
         log.info(f"make install directory:       {self.install_dir}")
         log.info(f"setuptools build directory:   {self.st_build_dir}")
         log.info(f"setuptools install directory: {setuptools_install_prefix}")
-        log.info(dedent(f"""
-        make-installed site-packages directory: {self.site_packages_dir}
-         (only relevant for copying files from 'make install directory'
-                                          to   'setuptools build directory'
-         """))
         log.info("-" * 3)
         log.info(f"Python executable: {self.py_executable}")
         log.info(f"Python includes:   {self.py_include_dir}")
@@ -508,7 +512,11 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
         log.info("-" * 3)
         if sys.platform == 'win32':
             log.info(f"OpenSSL dll directory: {OPTION['OPENSSL']}")
-        if sys.platform == 'darwin':
+        # for cross-compilation it is possible to use a macOS host, but
+        # pyside_macos_deployment_target is not relevant for the target.
+        # The only exception here is when we are trying to cross-compile from intel mac to m1 mac.
+        # This case is not supported yet.
+        if sys.platform == 'darwin' and not self.is_cross_compile:
             pyside_macos_deployment_target = (macos_pyside_min_deployment_target())
             log.info(f"MACOSX_DEPLOYMENT_TARGET set to: {pyside_macos_deployment_target}")
         log.info("=" * 30)
@@ -587,6 +595,15 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
             cmake_cmd.append(f"-DCMAKE_UNITY_BUILD_BATCH_SIZE={batch_size}")
             log.info("Using UNITY build")
 
+        if OPTION['SHIBOKEN_FORCE_PROCESS_SYSTEM_HEADERS']:
+            cmake_cmd.append("-DPYSIDE_TREAT_QT_INCLUDE_DIRS_AS_NON_SYSTEM=ON")
+            log.info("Shiboken will now process system Qt headers")
+
+        if OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS']:
+            extra_include_paths = ';'.join(OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS'].split(','))
+            cmake_cmd.append(f"-DSHIBOKEN_FORCE_PROCESS_SYSTEM_INCLUDE_PATHS={extra_include_paths}")
+            log.info(f"Shiboken will now process system headers from: {extra_include_paths}")
+
         cmake_cmd += [
             "-G", self.make_generator,
             f"-DBUILD_TESTS={self.build_tests}",
@@ -627,24 +644,11 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
                          f"Path given: {config_dir}")
 
         if OPTION["MODULE_SUBSET"]:
-            module_sub_set = ''
-            for m in OPTION["MODULE_SUBSET"].split(','):
-                if m.startswith('Qt'):
-                    m = m[2:]
-                if module_sub_set:
-                    module_sub_set += ';'
-                module_sub_set += m
-            cmake_cmd.append(f"-DMODULES={module_sub_set}")
+            cmake_cmd.append(f"-DMODULES={parse_modules(OPTION['MODULE_SUBSET'])}")
 
         if OPTION["SKIP_MODULES"]:
-            skip_modules = ''
-            for m in OPTION["SKIP_MODULES"].split(','):
-                if m.startswith('Qt'):
-                    m = m[2:]
-                if skip_modules:
-                    skip_modules += ';'
-                skip_modules += m
-            cmake_cmd.append(f"-DSKIP_MODULES={skip_modules}")
+            cmake_cmd.append(f"-DSKIP_MODULES={parse_modules(OPTION['SKIP_MODULES'])}")
+
         # Add source location for generating documentation
         cmake_src_dir = OPTION["QT_SRC"] if OPTION["QT_SRC"] else qt_src_dir
         if cmake_src_dir:
@@ -680,21 +684,28 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
             if OPTION['NO_OVERRIDE_OPTIMIZATION_FLAGS']:
                 cmake_cmd.append("-DQFP_NO_OVERRIDE_OPTIMIZATION_FLAGS=1")
 
-        if OPTION["LIMITED_API"] == "yes":
-            cmake_cmd.append("-DFORCE_LIMITED_API=yes")
-        elif OPTION["LIMITED_API"] == "no":
-            cmake_cmd.append("-DFORCE_LIMITED_API=no")
-        elif not OPTION["LIMITED_API"]:
+        if not OPTION["LIMITED_API"]:
             if sys.platform == 'win32' and self.debug:
                 cmake_cmd.append("-DFORCE_LIMITED_API=no")
         else:
-            raise SetupError("option limited-api must be 'yes' or 'no' "
-                             "(default yes if applicable, i.e. Python "
-                             "version >= 3.9 and release build if on Windows)")
+            if OPTION["LIMITED_API"].lower() in ("yes", "y", "1", "true"):
+                cmake_cmd.append("-DFORCE_LIMITED_API=yes")
+            elif OPTION["LIMITED_API"].lower() in ("no", "n", "0", "false"):
+                cmake_cmd.append("-DFORCE_LIMITED_API=no")
+            else:
+                raise SetupError(
+                    "Option '--limited-api' must be 'yes' or 'no'."
+                    f"Default is yes if Python version >= {get_allowed_python_versions()[0]} "
+                    "and Release build on Windows"
+                )
 
         if OPTION["DISABLE_PYI"]:
             cmake_cmd.append("-DDISABLE_PYI=yes")
 
+        if OPTION["UNOPTIMIZE"]:
+            value = OPTION["UNOPTIMIZE"]
+            cmake_cmd.append(f"-DSHIBOKEN_UNOPTIMIZE={value}")
+
         if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
             cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON")
         else:
@@ -745,7 +756,9 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
 
         cmake_cmd += platform_cmake_options()
 
-        if sys.platform == 'darwin':
+        # for a macOS host, cross-compilation is possible, but for the host system as such
+        # we only build shiboken. Hence the following code can be skipped.
+        if sys.platform == 'darwin' and not self.is_cross_compile:
             if OPTION["MACOS_ARCH"]:
                 # also tell cmake which architecture to use
                 cmake_cmd.append(f"-DCMAKE_OSX_ARCHITECTURES:STRING={OPTION['MACOS_ARCH']}")
@@ -823,6 +836,9 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
             # cross-compiling pyside.
             cmake_cmd.append(f"-DQFP_SHIBOKEN_TARGET_PATH={self.install_dir}")
 
+        if OPTION["SKIP_MYPY_TEST"]:
+            cmake_cmd.append("-DSKIP_MYPY_TEST=1")
+
         if self.cmake_toolchain_file:
             cmake_cmd.append(f"-DCMAKE_TOOLCHAIN_FILE={self.cmake_toolchain_file}")
 
@@ -842,13 +858,6 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
         if run_process(cmd_make) != 0:
             raise SetupError(f"Error compiling {extension}")
 
-        if sys.version_info == (3, 6) and sys.platform == "darwin":
-            # Python 3.6 has a Sphinx problem because of docutils 0.17 .
-            # Instead of pinning v0.16, setting the default encoding fixes that.
-            # Since other platforms are not affected, we restrict this to macOS.
-            if "UTF-8" not in os.environ.get("LC_ALL", ""):
-                os.environ["LC_ALL"] = "en_US.UTF-8"
-
         if OPTION["BUILD_DOCS"]:
             if extension.lower() == SHIBOKEN:
                 found = importlib.util.find_spec("sphinx")
@@ -1036,10 +1045,27 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
 
         if clang_lib_path.exists():
             basename = clang_lib_path.name
+            # In case of static libclang we don't need the lib file inside the wheel
+            if '.a' == clang_lib_path.suffix:
+                log.info("Skip copying libclang archive to the package.")
+                return
+
             log.info(f"Copying libclang shared library {clang_lib_path} to the package "
                      f"folder as {basename}.")
             destination_path = destination_dir / basename
 
+            # It is possible that the resolved libclang has a different SONAME
+            # For example the actual libclang might be named libclang.so.14.0.0 and its
+            # SONAME might be libclang.so.13
+            # In this case, the ideal approach is to find the SONAME and create a symlink to the
+            # actual libclang in the destination directory. But, Python packaging (setuptools)
+            # does not support symlinks.
+            # So, we rename the actual libclang to the SONAME and copy it to the destination
+            if sys.platform == 'linux':
+                soname = get_soname(clang_lib_path)
+                if soname and soname != clang_lib_path.name:
+                    destination_path = destination_path.parent / soname
+
             # Need to modify permissions in case file is not writable
             # (a reinstall would cause a permission denied error).
             copyfile(clang_lib_path,
@@ -1200,10 +1226,6 @@ class PysideBaseDocs(Command, CommandMixin):
     user_options = CommandMixin.mixin_user_options
 
     def __init__(self, *args, **kwargs):
-        if args[0].commands[0] == "build_rst_docs":
-            args[0].commands[0] = "build_base_docs"
-            log.warning("'build_rst_docs' is deprecated and will be removed. "
-                        "Please use 'build_base_docs' instead.")
         self.command_name = "build_base_docs"
         Command.__init__(self, *args, **kwargs)
         CommandMixin.__init__(self)
@@ -1280,21 +1302,42 @@ class PysideBaseDocs(Command, CommandMixin):
                 raise SetupError(f"Error running CMake for {self.doc_dir}")
 
             if self.name == PYSIDE:
+                def run_script(script_path, args=None):
+                    cmd = [sys.executable, os.fspath(script_path)]
+                    if args:
+                        cmd.extend(args)
+                    if run_process(cmd) != 0:
+                        raise SetupError(f"Error running {script_path}")
+
                 self.sphinx_src = self.out_dir / "base"
+                # Generates the .rst files from the examples
                 example_gallery = config.setup_script_dir / "tools" / "example_gallery" / "main.py"
-                assert example_gallery.is_file()
-                example_gallery_cmd = [sys.executable, os.fspath(example_gallery)]
-                if OPTION["LOG_LEVEL"] == LogLevel.QUIET:
-                    example_gallery_cmd.append("--quiet")
-                qt_src_dir = OPTION['QT_SRC']
-                if qt_src_dir:
-                    example_gallery_cmd.extend(["--qt-src-dir", qt_src_dir])
-                if run_process(example_gallery_cmd) != 0:
-                    raise SetupError(f"Error running example gallery for {self.doc_dir}")
+                if example_gallery.is_file():
+                    example_gallery_args = []
+                    if OPTION["LOG_LEVEL"] == LogLevel.QUIET:
+                        example_gallery_args.append("--quiet")
+                    qt_src_dir = OPTION['QT_SRC']
+                    if qt_src_dir:
+                        example_gallery_args.extend(["--qt-src-dir", qt_src_dir])
+                    run_script(example_gallery, example_gallery_args)
+                else:
+                    log.warning("Example gallery script for generating .rst for examples"
+                                f"not found: {example_gallery}")
+
+                # Generates the .rst files from the release notes
+                release_notes = config.setup_script_dir / "tools" / "release_notes" / "main.py"
+                if release_notes.is_file():
+                    release_notes_args = []
+                    if OPTION["LOG_LEVEL"] != LogLevel.QUIET:
+                        release_notes_args.append("--verbose")
+                    run_script(release_notes, release_notes_args)
+                else:
+                    log.warning("Release notes script for generating .rst for release notes"
+                                f"not found: {release_notes}")
             elif self.name == SHIBOKEN:
                 self.sphinx_src = self.out_dir
 
-            sphinx_cmd = ["sphinx-build", "-b", "html", "-j", "auto", "-c",
+            sphinx_cmd = ["sphinx-build", "-b", "html", "-j", "auto", "-n", "-c",
                           str(self.sphinx_src), str(self.doc_dir),
                           str(self.out_dir)]
             if run_process(sphinx_cmd) != 0:
@@ -1316,8 +1359,6 @@ cmd_class_dict = {
     'install': PysideInstall,
     'install_lib': PysideInstallLib,
     'build_base_docs': PysideBaseDocs,
-    # TODO: Remove build_rst_docs in the next version, see PYSIDE-2504
-    'build_rst_docs': PysideBaseDocs,
 }
 if wheel_module_exists:
     pyside_bdist_wheel = get_bdist_wheel_override()
index bfaf03262069622bac018c029ba9a554cb7864f3..5963a3982161402dce5f9670b6b0dda23dd72229 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from setuptools import Command
 
@@ -9,7 +10,7 @@ from pathlib import Path
 
 from .log import log, LogLevel
 from .qtinfo import QtInfo
-from .utils import memoize, which
+from .utils import memoize, which, Singleton
 
 _AVAILABLE_MKSPECS = ["ninja", "msvc", "mingw"] if sys.platform == "win32" else ["ninja", "make"]
 
@@ -25,6 +26,7 @@ Additional options:
   --shiboken-host-path                 Path to host shiboken package when cross-compiling
   --qt-host-path                       Path to host Qt installation when cross-compiling
   --disable-pyi                        Disable .pyi file generation
+  --skip-mypy-test                     Skip the mypy test which can take long in debug builds
 """
 
 
@@ -39,7 +41,7 @@ def _warn_deprecated_option(option, replacement=None):
     log.warning(w)
 
 
-class Options(object):
+class Options(metaclass=Singleton):
     def __init__(self):
 
         # Dictionary containing values of all the possible options.
@@ -101,88 +103,85 @@ class Options(object):
         self.dict[name] = value
         return value
 
+    def find_qtpaths(self):
+        # Skip the first run that will trigger the three different build
+        # stated of the setup process
+        if self.dict["internal-build-type"] is None:
+            return None
+        # for these command --qtpaths should not be required
+        no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs"]
 
-options = Options()
-
-
-def has_option(*args, **kwargs):
-    return options.has_option(*args, **kwargs)
-
-
-def option_value(*args, **kwargs):
-    return options.option_value(*args, **kwargs)
-
+        for no_qtpaths_command in no_qtpaths_commands:
+            if any(no_qtpaths_command in argument for argument in sys.argv):
+                return None
 
-def _jobs_option_value():
-    """Option value for parallel builds."""
-    value = option_value('parallel', short_option_name='j')
-    if value:
-        return f"-j{value}" if not value.startswith('-j') else value
-    return ''
+        qtpaths = self.option_value("qtpaths")
+        if qtpaths is not None:
+            return qtpaths
 
+        # if qtpaths is not given as cli option, try to find it in PATH
+        qtpaths = which("qtpaths6")
+        if qtpaths is not None:
+            return str(Path(qtpaths).resolve())
 
-def find_qtpaths():
-    # for these command --qtpaths should not be required
-    no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs",
-                           "build_rst_docs"]
+        qtpaths = which("qtpaths")
+        if qtpaths is not None:
+            return str(Path(qtpaths).resolve())
 
-    for no_qtpaths_command in no_qtpaths_commands:
-        if any(no_qtpaths_command in argument for argument in sys.argv):
-            return None
+        if qtpaths is None:
+            sys.exit(-1)
 
-    qtpaths = option_value("qtpaths")
-    if qtpaths:
         return qtpaths
 
-    # if qtpaths is not given as cli option, try to find it in PATH
-    qtpaths = which("qtpaths6")
-    if qtpaths:
-        return str(qtpaths.resolve())
-
-    qtpaths = which("qtpaths")
-    if qtpaths:
-        return str(qtpaths.resolve())
-
-    return qtpaths
-
-
-# Declare options which need to be known when instantiating the setuptools
-# commands or even earlier during SetupRunner.run().
-OPTION = {
-    "BUILD_TYPE": option_value("build-type"),
-    "INTERNAL_BUILD_TYPE": option_value("internal-build-type"),
-    # number of parallel build jobs
-    "JOBS": _jobs_option_value(),
-    # Legacy, not used any more.
-    "JOM": has_option('jom'),
-    "MACOS_USE_LIBCPP": has_option("macos-use-libc++"),
-    "LOG_LEVEL": option_value("log-level", remove=False),
-    "QUIET": has_option('quiet'),
-    "VERBOSE_BUILD": has_option('verbose-build'),
-    "SNAPSHOT_BUILD": has_option("snapshot-build"),
-    "LIMITED_API": option_value("limited-api"),
-    "DISABLE_PYI": has_option("disable-pyi"),
-    "PACKAGE_TIMESTAMP": option_value("package-timestamp"),
-    # This is used automatically by setuptools.command.install object, to
-    # specify the final installation location.
-    "FINAL_INSTALL_PREFIX": option_value("prefix", remove=False),
-    "CMAKE_TOOLCHAIN_FILE": option_value("cmake-toolchain-file"),
-    "SHIBOKEN_HOST_PATH": option_value("shiboken-host-path"),
-    "SHIBOKEN_HOST_PATH_QUERY_FILE": option_value("internal-shiboken-host-path-query-file"),
-    "QT_HOST_PATH": option_value("qt-host-path"),
-    # This is used to identify the template for doc builds
-    "QTPATHS": find_qtpaths()
-    # This is an optional command line option. If --qtpaths is not provided via command-line,
-    # then qtpaths is checked inside PATH variable
-}
-
-_deprecated_option_jobs = option_value('jobs')
-if _deprecated_option_jobs:
-    _warn_deprecated_option('jobs', 'parallel')
-    OPTION["JOBS"] = _deprecated_option_jobs
-
-
-class CommandMixin(object):
+    def _jobs_option_value(self):
+        """Option value for parallel builds."""
+        value = self.option_value('parallel', short_option_name='j')
+
+        _deprecated_option_jobs = self.option_value('jobs')
+        if _deprecated_option_jobs:
+            _warn_deprecated_option('jobs', 'parallel')
+            value = _deprecated_option_jobs
+
+        if value:
+            return f"-j{value}" if not value.startswith('-j') else value
+        return ''
+
+    def resolve(self):
+        return {
+            "BUILD_TYPE": self.option_value("build-type"),
+            "INTERNAL_BUILD_TYPE": self.option_value("internal-build-type"),
+            # number of parallel build jobs
+            "JOBS": self._jobs_option_value(),
+            # Legacy, not used any more.
+            "JOM": self.has_option('jom'),
+            "MACOS_USE_LIBCPP": self.has_option("macos-use-libc++"),
+            "LOG_LEVEL": self.option_value("log-level", remove=False),
+            "QUIET": self.has_option('quiet'),
+            "VERBOSE_BUILD": self.has_option('verbose-build'),
+            "SNAPSHOT_BUILD": self.has_option("snapshot-build"),
+            "LIMITED_API": self.option_value("limited-api"),
+            "UNOPTIMIZE": self.option_value("unoptimize"),
+            "DISABLE_PYI": self.has_option("disable-pyi"),
+            "SKIP_MYPY_TEST": self.has_option("skip-mypy-test"),
+            "PACKAGE_TIMESTAMP": self.option_value("package-timestamp"),
+            # This is used automatically by setuptools.command.install object, to
+            # specify the final installation location.
+            "FINAL_INSTALL_PREFIX": self.option_value("prefix", remove=False),
+            "CMAKE_TOOLCHAIN_FILE": self.option_value("cmake-toolchain-file"),
+            "SHIBOKEN_HOST_PATH": self.option_value("shiboken-host-path"),
+            "SHIBOKEN_HOST_PATH_QUERY_FILE": self.option_value(
+                "internal-shiboken-host-path-query-file"
+            ),
+            "QT_HOST_PATH": self.option_value("qt-host-path"),
+            # This is used to identify the template for doc builds
+            "QTPATHS": self.find_qtpaths()
+            # This is an optional command line option.
+            # If --qtpaths is not provided via command-line,
+            # then qtpaths is checked inside PATH variable
+        }
+
+
+class CommandMixin:
     """Mixin for the setuptools build/install commands handling the options."""
 
     _static_class_finalized_once = False
@@ -247,7 +246,27 @@ class CommandMixin(object):
         ('plat-name=', None, 'The platform name for which we are cross-compiling'),
         ('unity', None, 'Use CMake UNITY_BUILD_MODE (obsolete)'),
         ('no-unity', None, 'Disable CMake UNITY_BUILD_MODE'),
-        ('unity-build-batch-size=', None, 'Value of CMAKE_UNITY_BUILD_BATCH_SIZE')
+        ('unity-build-batch-size=', None, 'Value of CMAKE_UNITY_BUILD_BATCH_SIZE'),
+        # shiboken-force-process-system-headers option is specifically used to tell the clang
+        # inside shiboken to process the system headers, when building against a system Qt.
+        #
+        # This option is specific for Flatpak and OS distro builds of PySide6. So, use with
+        # caution as it may also try to parse other global headers.
+        ('shiboken-force-process-system-headers', None,
+         'When building PySide against system Qt, shiboken does not ignore the system Qt headers'),
+        # shiboken-extra-inlude-paths option is specifically used to tell the clang inside shiboken
+        # to include extra paths when parsing the headers. Use with caution.
+        ('shiboken-extra-include-paths=', None,
+         'Extra include paths for shiboken. Comma separated.'),
+        # flatpak option is used to build PySide6 for Flatpak. Flatpak is a special case where
+        # some of the headers for the Qt modules are located as system headers in /usr/include in
+        # the KDE flatpak SDK. Therefore --shiboken-force-process-system headers will be by
+        # default enabled when --flatpak is enabled.
+        # Apart from that, headers for certain Qt modules like QtWebEngine, QtPdf etc. are located
+        # in /app/include from the Flapak WebEngine baseapp. Therefore when the --flatpak option is
+        # enabled, the extra include path of /app/include will be added to the option
+        # --shiboken-extra-include-paths.
+        ('flatpak', None, 'Build PySide6 for Flatpak.'),
     ]
 
     def __init__(self):
@@ -288,7 +307,7 @@ class CommandMixin(object):
         self.shiboken_target_path = None
         self.python_target_path = None
         self.is_cross_compile = False
-        self.cmake_toolchain_file = None
+        self.cmake_toolchain_file: str = ""
         self.make_spec = None
         self.macos_arch = None
         self.macos_sysroot = None
@@ -309,6 +328,9 @@ class CommandMixin(object):
         self.unity = False
         self.no_unity = False
         self.unity_build_batch_size = "16"
+        self.shiboken_force_process_system_headers = False
+        self.shiboken_extra_include_paths = None
+        self.flatpak = False
 
         # When initializing a command other than the main one (so the
         # first one), we need to copy the user options from the main
@@ -357,7 +379,7 @@ class CommandMixin(object):
         # because we DON'T want those to be found when cross compiling.
         # Currently when cross compiling, qt-target-path MUST be used.
         using_cmake_toolchain_file = False
-        cmake_toolchain_file = None
+        cmake_toolchain_file: str = ""
         if OPTION["CMAKE_TOOLCHAIN_FILE"]:
             self.is_cross_compile = True
             using_cmake_toolchain_file = True
@@ -429,6 +451,12 @@ class CommandMixin(object):
                         "Unity build mode is now the default.")
         OPTION['UNITY'] = not self.no_unity
         OPTION['UNITY_BUILD_BATCH_SIZE'] = self.unity_build_batch_size
+        OPTION['SHIBOKEN_FORCE_PROCESS_SYSTEM_HEADERS'] = self.shiboken_force_process_system_headers
+        OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS'] = self.shiboken_extra_include_paths
+        OPTION['FLATPAK'] = self.flatpak
+        if OPTION['FLATPAK']:
+            OPTION['SHIBOKEN_FORCE_PROCESS_SYSTEM_HEADERS'] = True
+            OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS'] = '/app/include'
 
         qtpaths_abs_path = None
         if self.qtpaths and Path(self.qtpaths).exists():
@@ -461,18 +489,17 @@ class CommandMixin(object):
                        qt_target_path=qt_target_path,
                        cmake_toolchain_file=cmake_toolchain_file)
 
-        if 'build_base_docs' not in sys.argv and 'build_rst_docs' not in sys.argv:
+        if 'build_base_docs' not in sys.argv:
             try:
                 QtInfo().prefix_dir
             except Exception as e:
                 if not self.qt_target_path:
                     log.error(
-                        "\nCould not find Qt. You can pass the --qt-target-path=<qt-dir> option "
-                        "as a hint where to find Qt. Error was:\n\n\n")
+                        "Could not find Qt. You can pass the --qt-target-path=<qt-dir> option "
+                        "as a hint where to find Qt.\n")
                 else:
-                    log.error(
-                        f"\nCould not find Qt via provided option --qt-target-path={qt_target_path}"
-                        "Error was:\n\n\n")
+                    log.error("Could not find Qt via provided option "
+                              f"--qt-target-path={qt_target_path}\n")
                 raise e
 
         OPTION['CMAKE'] = self.cmake.resolve()
@@ -551,9 +578,8 @@ class CommandMixin(object):
         # while cross-compiling.
         # Skip this process for the 'build_base_docs' command
         if (not self.is_cross_compile
-                and not self.qt_target_path
-                and 'build_base_docs' not in sys.argv
-                and 'build_rst_docs' not in sys.argv):
+            and not self.qt_target_path
+                and 'build_base_docs' not in sys.argv):
             # Enforce usage of qmake in QtInfo if it was given explicitly.
             if self.qmake:
                 self.has_qmake_option = True
@@ -598,3 +624,7 @@ class CommandMixin(object):
             return False
 
         return True
+
+
+# OPTION dictionary that will be imported in other build_scripts
+OPTION = Options().resolve()
index 853aaad7bce71a5022d8e5b00d948fa164c84704..498a6e52bdd963087fa687306062ec0d47dfe5a2 100644 (file)
@@ -1,2 +1,3 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
index b4c66d94ed214ce0d14aaa52aa185ed47cc3ce57..32ad9daca4a875f37dcf9755ecaeb2a216d795c9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from pathlib import Path
 
index dbe60d3433bd578ce63f99f0cbf9ed859354caf2..31d552786514e7ae6735ca825c6d19ef0fb5a36c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import fnmatch
 from pathlib import Path
@@ -21,7 +22,7 @@ def _macos_patch_executable(name, _vars=None):
     macos_add_rpath(rpath, binary)
 
 
-def prepare_standalone_package_macos(pyside_build, _vars):
+def prepare_standalone_package_macos(pyside_build, _vars, is_android=False):
     built_modules = _vars['built_modules']
 
     constrain_modules = None
@@ -119,7 +120,11 @@ def prepare_standalone_package_macos(pyside_build, _vars):
         ignored_modules = []
         if not pyside_build.is_webengine_built(built_modules):
             ignored_modules.extend(['libQt6WebEngine*.dylib'])
+
         accepted_modules = ['libQt6*.6.dylib']
+        if is_android:
+            accepted_modules = ['libQt6*.so', '*-android-dependencies.xml']
+
         if constrain_modules:
             accepted_modules = [f"libQt6{module}*.6.dylib" for module in constrain_modules]
 
@@ -156,6 +161,8 @@ def prepare_standalone_package_macos(pyside_build, _vars):
         # <qt>/plugins/* -> <setup>/{st_package_name}/Qt/plugins
         plugins_target = destination_qt_dir / "plugins"
         filters = ["*.dylib"]
+        if is_android:
+            filters = ["*.so"]
         copydir("{qt_plugins_dir}", plugins_target,
                 _filter=filters,
                 recursive=True,
index 3333f5f96462ca9d55102355ce89a1c4cd28487b..35d6dd1c3cc20ba22fdb15206c4a64cc93599a77 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import sys
 from pathlib import Path
@@ -11,7 +12,8 @@ from ..utils import copydir, copyfile, copy_qt_metatypes, makefile
 from .. import PYSIDE, SHIBOKEN
 from .linux import prepare_standalone_package_linux
 from .macos import prepare_standalone_package_macos
-from .. import PYSIDE_UNIX_BIN_TOOLS, PYSIDE_UNIX_LIBEXEC_TOOLS, PYSIDE_UNIX_BUNDLED_TOOLS
+from .. import (PYSIDE_UNIX_BIN_TOOLS, PYSIDE_UNIX_LIBEXEC_TOOLS, PYSIDE_UNIX_BUNDLED_TOOLS,
+                PYSIDE_MULTIMEDIA_LIBS)
 
 
 def _macos_copy_gui_executable(name, _vars=None):
@@ -122,9 +124,9 @@ def prepare_packages_posix(pyside_build, _vars, cross_build=False):
             scripts = ["pyside_tool.py", "metaobjectdump.py", "project.py", "qml.py",
                        "qtpy2cpp.py", "deploy.py"]
 
-            script_dirs = ["qtpy2cpp_lib", "deploy_lib", "project"]
+            script_dirs = ["qtpy2cpp_lib", "deploy_lib", "project_lib"]
 
-            if sys.platform.startswith("linux"):
+            if sys.platform in ["linux", "darwin"]:
                 scripts.append("android_deploy.py")
                 scripts.append("requirements-android.txt")
                 script_dirs.extend(["deploy_lib/android",
@@ -198,10 +200,10 @@ def prepare_packages_posix(pyside_build, _vars, cross_build=False):
 
             # <install>/share/{st_package_name}/glue/* ->
             #   <setup>/{st_package_name}/glue
-            copydir(
-                "{install_dir}/share/{st_package_name}/glue",
-                "{st_build_dir}/{st_package_name}/glue",
-                _vars=_vars)
+            for glue in ['glue', 'doc']:
+                source = "{install_dir}/share/{st_package_name}/" + glue
+                target = "{st_build_dir}/{st_package_name}/" + glue
+                copydir(source, target, _vars=_vars)
 
         if not is_android:
             # <source>/pyside6/{st_package_name}/support/* ->
@@ -232,12 +234,20 @@ def prepare_packages_posix(pyside_build, _vars, cross_build=False):
                 "{st_build_dir}/{st_package_name}/jar",
                 _vars=_vars)
 
+    # Some libraries specific to Linux/Android from 6.8
+    # eg: the libav* libraries are required for the multimedia module
+    if config.is_internal_pyside_build() and sys.platform != "darwin":
+        qt_multimedia_filters = [f"lib{lib}*.so*" for lib in PYSIDE_MULTIMEDIA_LIBS]
+        copydir("{qt_lib_dir}", destination_qt_dir / "lib",
+                _filter=qt_multimedia_filters,
+                recursive=False, _vars=_vars, force_copy_symlinks=True)
+
     # Copy Qt libs to package
     if OPTION["STANDALONE"]:
         if config.is_internal_pyside_build() or config.is_internal_shiboken_generator_build():
             _vars['built_modules'] = generated_config['built_modules']
             if sys.platform == 'darwin':
-                prepare_standalone_package_macos(pyside_build, _vars)
+                prepare_standalone_package_macos(pyside_build, _vars, is_android=is_android)
             else:
                 prepare_standalone_package_linux(pyside_build, _vars, cross_build,
                                                  is_android=is_android)
index 9c29953be2acdbe6c4c4b4f62db2df4023f034fb..eaab7acb6780ef73698d1615002f2bee17409557 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import functools
 import os
@@ -11,8 +12,8 @@ from ..log import log
 from ..config import config
 from ..options import OPTION
 from ..utils import (copydir, copyfile, copy_qt_metatypes,
-                     download_and_extract_7z, filter_match, makefile)
-from .. import PYSIDE, SHIBOKEN, PYSIDE_WINDOWS_BIN_TOOLS
+                     download_and_extract_7z, filter_match, makefile, in_coin)
+from .. import PYSIDE, SHIBOKEN, PYSIDE_WINDOWS_BIN_TOOLS, PYSIDE_MULTIMEDIA_LIBS
 
 
 def prepare_packages_win32(pyside_build, _vars):
@@ -116,7 +117,7 @@ def prepare_packages_win32(pyside_build, _vars):
             target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script}"
             copyfile(src, target, force=False, _vars=_vars)
 
-        for script_dir in ("qtpy2cpp_lib", "deploy_lib", "project"):
+        for script_dir in ("qtpy2cpp_lib", "deploy_lib", "project_lib"):
             src = f"{{install_dir}}/bin/{script_dir}"
             target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script_dir}"
             # Exclude subdirectory tests
@@ -152,10 +153,9 @@ def prepare_packages_win32(pyside_build, _vars):
 
         # <install>/share/{st_package_name}/glue/* ->
         #   <setup>/{st_package_name}/glue
-        copydir(
-            "{install_dir}/share/{st_package_name}/glue",
-            destination_dir / "glue",
-            _vars=_vars)
+        for glue in ['glue', 'doc']:
+            source = "{install_dir}/share/{st_package_name}/" + glue
+            copydir(source, destination_dir / glue, _vars=_vars)
 
         # <source>/pyside6/{st_package_name}/support/* ->
         #   <setup>/{st_package_name}/support/*
@@ -217,8 +217,7 @@ msvc_redist = [
 
 
 def copy_msvc_redist_files(destination_dir):
-    in_coin = os.environ.get('COIN_LAUNCH_PARAMETERS', None)
-    if in_coin is None:
+    if not in_coin():
         log.info("Qt dependency DLLs (MSVC redist) will not be copied.")
         return
 
@@ -244,18 +243,15 @@ def copy_msvc_redist_files(destination_dir):
         raise FileNotFoundError(msg)
 
 
-def copy_qt_dependency_dlls(_vars, destination_qt_dir, artifacts):
+def download_qt_dependency_dlls(_vars, destination_qt_dir, artifacts):
     # Extract Qt dependency dlls when building on Qt CI.
-    in_coin = os.environ.get('COIN_LAUNCH_PARAMETERS', None)
-    if in_coin is None:
+    if not in_coin():
         log.info("Qt dependency DLLs will not be downloaded and extracted.")
         return
 
     with tempfile.TemporaryDirectory() as temp_path:
         redist_url = "https://download.qt.io/development_releases/prebuilt/vcredist/"
-        zip_file = "pyside_qt_deps_64_2019.7z"
-        if "{target_arch}".format(**_vars) == "32":
-            zip_file = "pyside_qt_deps_32_2019.7z"
+        zip_file = "pyside_qt_deps_681_64_2022.7z"
         try:
             download_and_extract_7z(redist_url + zip_file, temp_path)
         except Exception as e:
@@ -284,40 +280,31 @@ def copy_qt_artifacts(pyside_build, destination_qt_dir, copy_pdbs, _vars):
         copy_translations = False
         copy_qt_conf = False
         copy_qt_permanent_artifacts = False
-        copy_clang = True
 
     # <qt>/bin/*.dll and Qt *.exe -> <setup>/{st_package_name}
-    qt_artifacts_permanent = [
-        "avcodec-60.dll",
-        "avformat-60.dll",
-        "avutil-58.dll",
-        "swresample-4.dll",
-        "swscale-7.dll",
-        "opengl*.dll",
-        "designer.exe",
-        "linguist.exe",
-        "lrelease.exe",
-        "lupdate.exe",
-        "lconvert.exe",
-        "qtdiag.exe"
-    ]
-
-    # Choose which EGL library variants to copy.
-    qt_artifacts_egl = [
-        "libEGL{}.dll",
-        "libGLESv2{}.dll"
-    ]
-    if pyside_build.qtinfo.build_type != 'debug_and_release':
-        egl_suffix = '*'
-    elif pyside_build.debug:
-        egl_suffix = 'd'
-    else:
-        egl_suffix = ''
-    qt_artifacts_egl = [a.format(egl_suffix) for a in qt_artifacts_egl]
+    qt_multimedia_artifacts_permanent = [f"{lib}-*.dll" for lib in PYSIDE_MULTIMEDIA_LIBS]
+
+    def qt_rhi_artifacts_permanent():
+        result = ["opengl*.dll"]
+        if pyside_build.qtinfo.build_type != 'debug_and_release':
+            suffix = '*'
+        elif pyside_build.debug:
+            suffix = 'd'
+        else:
+            suffix = ''
+        result.append(f"libEGL{suffix}.dll")  # Needed in Qt 6 (ANGLE)?
+        result.append(f"libGLESv2{suffix}.dll")
+        return result
 
     if copy_qt_permanent_artifacts:
-        artifacts = qt_artifacts_permanent + qt_artifacts_egl
-        copy_qt_dependency_dlls(_vars, destination_qt_dir, artifacts)
+        download_qt_dependency_dlls(_vars, destination_qt_dir, qt_rhi_artifacts_permanent())
+        if not copydir("{qt_bin_dir}", destination_qt_dir,
+                       _filter=qt_multimedia_artifacts_permanent,
+                       recursive=False, _vars=_vars):
+            msg = "The multimedia artifacts were not found."
+            log.warning(msg)
+            if in_coin():  # Allow local developer builds without -DQT_DEPLOY_FFMPEG...
+                raise FileNotFoundError(msg)
 
     # <qt>/bin/*.dll and Qt *.pdbs -> <setup>/{st_package_name} part two
     # File filter to copy only debug or only release files.
index abaf48fc8ee84d23cf83867b080e5605aad9778b..01c05f144a292e9d47e2c59015acb6dac59481e3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import datetime
 import os
@@ -11,7 +12,6 @@ import warnings
 from argparse import ArgumentParser, RawTextHelpFormatter
 from enum import Enum, auto
 from pathlib import Path
-from typing import List
 
 DESC = """
 Utility script for working with Qt for Python.
@@ -101,14 +101,14 @@ def which(needle: str):
     return None
 
 
-def command_log_string(args: List[str], directory: Path):
+def command_log_string(args: list[str], directory: Path):
     result = f'[{directory.name}]'
     for arg in args:
         result += f' "{arg}"' if ' ' in arg else f' {arg}'
     return result
 
 
-def execute(args: List[str]):
+def execute(args: list[str]):
     """Execute a command and print to log"""
     log_string = command_log_string(args, Path.cwd())
     print(log_string)
@@ -172,7 +172,7 @@ def edit_config_file():
 """
 Config file handling, cache and read function
 """
-config_dict = {}
+config_dict: dict = {}
 
 
 def read_config_file(file_name):
@@ -263,15 +263,15 @@ def read_config_python_binary() -> str:
 
 def get_config_file(base_name) -> Path:
     global user
-    home = os.getenv('HOME')
+    home = os.getenv('HOME', default="")
     if IS_WINDOWS:
         # Set a HOME variable on Windows such that scp. etc.
         # feel at home (locating .ssh).
         if not home:
-            home = os.getenv('HOMEDRIVE') + os.getenv('HOMEPATH')
+            home = os.getenv('HOMEDRIVE', default="") + os.getenv('HOMEPATH', default="")
             os.environ['HOME'] = home
         user = os.getenv('USERNAME')
-        config_file = Path(os.getenv('APPDATA')) / base_name
+        config_file = Path(os.getenv('APPDATA', default="")) / base_name
     else:
         user = os.getenv('USER')
         config_dir = Path(home) / '.config'
@@ -290,7 +290,7 @@ def build(target: str):
     acceleration = read_acceleration_config()
     if not IS_WINDOWS and acceleration == Acceleration.INCREDIBUILD:
         arguments.append(INCREDIBUILD_CONSOLE)
-        arguments.appendh('--avoid')  # caching, v0.96.74
+        arguments.append('--avoid')  # caching, v0.96.74
     arguments.extend([read_config_python_binary(), 'setup.py', target])
     build_arguments = read_config_build_arguments()
     if opt_verbose and LOG_LEVEL_OPTION in build_arguments:
@@ -341,7 +341,7 @@ def run_tests():
     """Run tests redirected into a log file with a time stamp"""
     logfile_name = datetime.datetime.today().strftime("test_%Y%m%d_%H%M.txt")
     binary = sys.executable
-    command = f'"{binary}" testrunner.py test > {logfile_name}'
+    command = f'"{binary}" testrunner.py test --reruns 2 > {logfile_name}'
     print(command_log_string([command], Path.cwd()))
     start_time = time.time()
     result = 0 if opt_dry_run else os.system(command)
index 1eb7c490997efef980faddd876d28f4161a61fcf..352fad460ecad487e007024986fc89954a6e2e31 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import subprocess
@@ -9,7 +10,7 @@ from .utils import (configure_cmake_project, parse_cmake_project_message_info,
                     platform_cmake_options)
 
 
-class QtInfo(object):
+class QtInfo:
     _instance = None  # singleton helpers
 
     def __new__(cls):  # __new__ always a classmethod
@@ -31,7 +32,7 @@ class QtInfo(object):
             self._force_qmake = False
             self._use_cmake = False
             self._qt_target_path = None
-            self._cmake_toolchain_file = None
+            self._cmake_toolchain_file: str = ""
             # Dict to cache qmake values.
             self._query_dict = {}
 
index 9bcbb4afe271ca28905522850f1b9b10012d4ca3..ec679801e031309f284bc3171218660ff3a0c9cf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import sys
@@ -18,7 +19,7 @@ from build_scripts.utils import run_process
 from build_scripts.log import log, LogLevel
 
 
-class SetupRunner(object):
+class SetupRunner:
     def __init__(self, orig_argv):
         self.invocations_list = []
 
@@ -93,7 +94,7 @@ class SetupRunner(object):
                 setup_cmd.append(self.construct_cmd_line_argument(name, value))
 
         # Add --reuse-build option if requested and not already present.
-        if (reuse_build and command in ('bdist_wheel', 'build', 'build_base_docs', 'build_rst_docs', 'install')
+        if (reuse_build and command in ('bdist_wheel', 'build', 'build_base_docs', 'install')
                 and not self.cmd_line_argument_is_in_args("reuse-build", modified_argv)):
             setup_cmd.append(self.construct_cmd_line_argument("reuse-build"))
         return setup_cmd
@@ -187,7 +188,7 @@ class SetupRunner(object):
                            setup_script_dir=self.setup_script_dir,
                            cmake_toolchain_file=OPTION["CMAKE_TOOLCHAIN_FILE"],
                            log_level=OPTION["LOG_LEVEL"],
-                           qt_install_path=qt_install_path)
+                           qt_install_dir=qt_install_path)
 
         # Enable logging for both the top-level invocation of setup.py
         # as well as for child invocations. We we now use
index 74d9e6fc5ac19718b13a9f9965e27c59e38725ef..29f2545d08dd864f99b95c573fec8fd38b685279 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import errno
 import fnmatch
@@ -11,7 +12,8 @@ import stat
 import subprocess
 import sys
 import tempfile
-import urllib.request as urllib
+import urllib.request
+import urllib.error
 from collections import defaultdict
 from pathlib import Path
 from textwrap import dedent, indent
@@ -27,6 +29,15 @@ except NameError:
     WindowsError = None
 
 
+class Singleton(type):
+    _instances = {}
+
+    def __call__(cls, *args, **kwargs):
+        if cls not in cls._instances:
+            cls._instances[cls] = super().__call__(*args, **kwargs)
+        return cls._instances[cls]
+
+
 def which(name):
     """
     Like shutil.which, but accepts a string or a PathLike and returns a Path
@@ -36,9 +47,8 @@ def which(name):
         if isinstance(name, Path):
             name = str(name)
         path = shutil.which(name)
-        if path is None:
-            raise TypeError("None was returned")
-        path = Path(path)
+        if path is not None:
+            path = Path(path)
     except TypeError as e:
         log.error(f"{name} was not found in PATH: {e}")
     return path
@@ -71,7 +81,12 @@ def get_numpy_location():
         if 'site-' in p:
             numpy = Path(p).resolve() / 'numpy'
             if numpy.is_dir():
-                return os.fspath(numpy / 'core' / 'include')
+                candidate = numpy / '_core' / 'include'  # Version 2
+                if not candidate.is_dir():
+                    candidate = numpy / 'core' / 'include'  # Version 1
+                if candidate.is_dir():
+                    return os.fspath(candidate)
+                log.warning(f"Cannot find numpy include dir under {numpy}")
     return None
 
 
@@ -119,6 +134,7 @@ def copyfile(src, dst, force=True, _vars=None, force_copy_symlink=False,
 
     # We use 'strict=False' to mimic os.path.realpath in case
     # the directory doesn't exist.
+    # TODO: This code can probably be removed when 'python setup.py install' usage is removed.
     link_target_path = src.resolve(strict=False)
     if link_target_path.parent == src.parent:
         link_target = Path(link_target_path.name)
@@ -129,7 +145,7 @@ def copyfile(src, dst, force=True, _vars=None, force_copy_symlink=False,
             os.chdir(target_dir)
             if link_name.exists():
                 if (link_name.is_symlink()
-                        and os.readlink(link_name) == link_target):
+                        and os.readlink(link_name) == str(link_target)):
                     log.info(f"Symlink already exists\n  {link_name} ->\n  {link_target}")
                     return dst
                 os.remove(link_name)
@@ -511,9 +527,9 @@ def download_and_extract_7z(fileurl, target):
     for i in range(1, 10):
         try:
             log.info(f"Downloading fileUrl {fileurl}, attempt #{i}")
-            localfile, info = urllib.urlretrieve(fileurl)
+            localfile, info = urllib.request.urlretrieve(fileurl)
             break
-        except urllib.URLError:
+        except urllib.error.URLError:
             pass
     if not localfile:
         log.error(f"Error downloading {fileurl} : {info}")
@@ -789,6 +805,12 @@ def linux_run_read_elf(executable_path):
 def linux_set_rpaths(patchelf, executable_path, rpath_string):
     """ Patches the `executable_path` with a new rpath string. """
 
+    path = Path(executable_path)
+    mode = path.stat().st_mode
+    if (mode & stat.S_IWUSR) == 0:
+        log.info(f"patchelf: {executable_path} is read-only, making writeable.")
+        path.chmod(mode | stat.S_IWUSR)
+
     cmd = [str(patchelf), '--set-rpath', str(rpath_string), str(executable_path)]
 
     if run_process(cmd) != 0:
@@ -932,48 +954,6 @@ def get_python_dict(python_script_path):
         raise
 
 
-def get_qtci_virtualEnv(python_ver, host, hostArch, targetArch):
-    _pExe = "python"
-    _env = f"{os.environ.get('PYSIDE_VIRTUALENV') or 'env'+python_ver}"
-    env_python = f"{_env}/bin/python"
-    env_pip = f"{_env}/bin/pip"
-
-    if host == "Windows":
-        log.info("New virtualenv to build {targetArch} in {hostArch} host")
-        _pExe = "python.exe"
-        # With windows we are creating building 32-bit target in 64-bit host
-        if hostArch == "X86_64" and targetArch == "X86":
-            if python_ver.startswith("3"):
-                var = f"PYTHON{python_ver}-32_PATH"
-                log.info(f"Try to find python from {var} env variable")
-                _path = Path(os.getenv(var, ""))
-                _pExe = _path / "python.exe"
-                if not _pExe.is_file():
-                    log.warning(f"Can't find python.exe from {_pExe}, using default python3")
-                    _pExe = Path(os.getenv("PYTHON3_32_PATH")) / "python.exe"
-            else:
-                _pExe = Path(os.getenv("PYTHON2_32_PATH")) / "python.exe"
-        else:
-            if python_ver.startswith("3"):
-                var = f"PYTHON{python_ver}-64_PATH"
-                log.info(f"Try to find python from {var} env variable")
-                _path = Path(os.getenv(var, ""))
-                _pExe = _path / "python.exe"
-                if not _pExe.is_file():
-                    log.warning(f"Can't find python.exe from {_pExe}, using default python3")
-                    _pExe = Path(os.getenv("PYTHON3_PATH")) / "python.exe"
-        env_python = f"{_env}\\Scripts\\python.exe"
-        env_pip = f"{_env}\\Scripts\\pip.exe"
-    else:
-        _pExe = f"python{python_ver}"
-        try:
-            run_instruction([_pExe, "--version"], f"Failed to guess python version {_pExe}")
-        except Exception as e:
-            print(f"Exception {type(e).__name__}: {e}")
-            _pExe = "python3"
-    return (_pExe, _env, env_pip, env_python)
-
-
 def run_instruction(instruction, error, initial_env=None):
     if initial_env is None:
         initial_env = os.environ
@@ -984,26 +964,6 @@ def run_instruction(instruction, error, initial_env=None):
         exit(result)
 
 
-def get_ci_qtpaths_path(ci_install_dir, ci_host_os):
-    qtpaths_path = f"--qtpaths={ci_install_dir}"
-    if ci_host_os == "MacOS":
-        return f"{qtpaths_path}/bin/qtpaths"
-    elif ci_host_os == "Windows":
-        return f"{qtpaths_path}\\bin\\qtpaths.exe"
-    else:
-        return f"{qtpaths_path}/bin/qtpaths"
-
-
-def get_ci_qmake_path(ci_install_dir, ci_host_os):
-    qmake_path = f"--qmake={ci_install_dir}"
-    if ci_host_os == "MacOS":
-        return f"{qmake_path}/bin/qmake"
-    elif ci_host_os == "Windows":
-        return f"{qmake_path}\\bin\\qmake.exe"
-    else:
-        return f"{qmake_path}/bin/qmake"
-
-
 def parse_cmake_conf_assignments_by_key(source_dir):
     """
         Parses a .cmake.conf file that contains set(foo "bar") assignments
@@ -1161,3 +1121,18 @@ def copy_qt_metatypes(destination_qt_dir, _vars):
     copydir(qt_meta_types_dir, destination_qt_dir / rel_meta_data_dir,
             _filter=["*.json"],
             recursive=False, _vars=_vars, force_copy_symlinks=True)
+
+
+def in_coin():
+    return os.environ.get('COIN_UNIQUE_JOB_ID', None) is not None
+
+
+def parse_modules(modules: str) -> str:
+    module_sub_set = ""
+    for m in modules.split(','):
+        if m.startswith('Qt'):
+            m = m[2:]
+        if module_sub_set:
+            module_sub_set += ';'
+        module_sub_set += m
+    return module_sub_set
index 2112bba9af35c2be5ef2ac5b7d91dc3762975a41..7ee04a7be259b07c5a1659c3ef5268c90be61dac 100644 (file)
@@ -1,15 +1,16 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 
 import json
 import sys
 from dataclasses import Field, dataclass, field
-from typing import Dict, List
 
 
 _pyside_package_path = None
 _module_json_file_path = None
+_multimedia_libs = ["avcodec", "avformat", "avutil", "swresample", "swscale"]
 
 
 def set_pyside_package_path(p):
@@ -49,23 +50,24 @@ class ModuleData:
     name: str
     ext: str = ""
     # Libraries not related to Qt modules
-    lib: List[str] = field(default_factory=list)
+    lib: list[str] = field(default_factory=list)
     # Libraries related to Qt modules
-    qtlib: List[str] = field(default_factory=list)
+    qtlib: list[str] = field(default_factory=list)
     # Files from the Qt/qml directory
-    qml: List[str] = field(default_factory=list)
-    pyi: List[str] = field(default_factory=list)
-    translations: List[str] = field(default_factory=list)
-    typesystems: List[str] = field(default_factory=list)
-    include: List[str] = field(default_factory=list)
-    glue: List[str] = field(default_factory=list)
-    metatypes: List[str] = field(default_factory=list)
-    plugins: List[str] = field(default_factory=list)
+    qml: list[str] = field(default_factory=list)
+    pyi: list[str] = field(default_factory=list)
+    translations: list[str] = field(default_factory=list)
+    typesystems: list[str] = field(default_factory=list)
+    include: list[str] = field(default_factory=list)
+    glue: list[str] = field(default_factory=list)
+    doc_glue: list[str] = field(default_factory=list)
+    metatypes: list[str] = field(default_factory=list)
+    plugins: list[str] = field(default_factory=list)
 
     # For special cases when a file/directory doesn't fall into
     # the previous categories.
-    extra_dirs: List[str] = field(default_factory=list)
-    extra_files: List[str] = field(default_factory=list)
+    extra_dirs: list[str] = field(default_factory=list)
+    extra_files: list[str] = field(default_factory=list)
 
     # Once the object is created, this method will be executed
     # and automatically will initialize some of the files that are
@@ -87,6 +89,7 @@ class ModuleData:
         self.typesystems.append(f"typesystem_{_lo}.xml")
         self.include.append(f"Qt{self.name}/*.h")
         self.glue.append(f"qt{_lo}.cpp")
+        self.doc_glue.append(f"qt{_lo}.rst")
         if not len(self.metatypes):
             self.metatypes.append(f"qt6{_lo}_relwithdebinfo_metatypes.json")
 
@@ -122,6 +125,7 @@ class ModuleData:
         self.typesystems = [f"typesystems/{i}" for i in self.typesystems]
         self.include = [f"include/{i}" for i in self.include]
         self.glue = [f"glue/{i}" for i in self.glue]
+        self.doc_glue = [f"doc/{i}" for i in self.doc_glue]
 
     def macos_pyside_wrappers_lib(self, s):
         if s.startswith("Qt"):
@@ -130,7 +134,7 @@ class ModuleData:
             return f"{s}.*{self.ext}*"
 
     @classmethod
-    def get_fields(cls) -> Dict[str, Field]:
+    def get_fields(cls) -> dict[str, Field]:
         return cls.__dataclass_fields__
 
     @staticmethod
@@ -150,7 +154,7 @@ class ModuleData:
 # for each module that will be included in the wheel.
 
 # PySide wheel
-def wheel_files_pyside_essentials() -> List[ModuleData]:
+def wheel_files_pyside_essentials() -> list[ModuleData]:
     files = [
         module_QtCore(),
         module_QtGui(),
@@ -186,7 +190,7 @@ def wheel_files_pyside_essentials() -> List[ModuleData]:
 
 
 # PySide Addons wheel
-def wheel_files_pyside_addons() -> List[ModuleData]:
+def wheel_files_pyside_addons() -> list[ModuleData]:
     files = [
         module_Qt3DAnimation(),
         module_Qt3DCore(),
@@ -199,6 +203,7 @@ def wheel_files_pyside_addons() -> List[ModuleData]:
         module_QtCharts(),
         module_QtDataVisualization(),
         module_QtGraphs(),
+        module_QtGraphsWidgets(),
         module_QtMultimedia(),
         module_QtMultimediaWidgets(),
         module_QtNetworkAuth(),
@@ -224,6 +229,10 @@ def wheel_files_pyside_addons() -> List[ModuleData]:
         module_QtHttpServer(),
         module_QtLocation(),
         module_QtAsyncio(),
+        module_QtWebView(),
+        # This is not an actual module, but it's required in order
+        # to add the 'Quick' components of the WebView.
+        module_QtWebViewQuick(),
     ]
     return files
 
@@ -462,7 +471,9 @@ def module_QtQml() -> ModuleData:
         "libQt6QmlNetwork",
         "libQt6QmlWorkerScript",
         "libQt6QmlXmlListModel",
-        "libQt6QmlCompiler"
+        "libQt6QmlCompiler",
+        "libQt6QmlMeta",
+        "libQt6LabsPlatform",
     ]
 
     _include = [
@@ -489,6 +500,8 @@ def module_QtQml() -> ModuleData:
         "qt6qmlmodels_relwithdebinfo_metatypes.json",
         "qt6qmlworkerscript_relwithdebinfo_metatypes.json",
         "qt6qmlxmllistmodel_relwithdebinfo_metatypes.json",
+        "qt6qmlmeta_relwithdebinfo_metatypes.json",
+        "qt6labsplatform_relwithdebinfo_metatypes.json",
     ]
 
     _qml = [
@@ -549,6 +562,8 @@ def module_QtQuick() -> ModuleData:
         "qt6quicktest_relwithdebinfo_metatypes.json",
         "qt6quicktestutilsprivate_relwithdebinfo_metatypes.json",
         "qt6quicktimeline_relwithdebinfo_metatypes.json",
+        "qt6quickvectorimage_relwithdebinfo_metatypes.json",
+        "qt6quickvectorimagegeneratorprivate_relwithdebinfo_metatypes.json",
     ]
     _qtlib = [
         "libQt6QuickEffects",
@@ -562,15 +577,15 @@ def module_QtQuick() -> ModuleData:
         "libQt6QuickTest",
         "libQt6QuickTimeline",
         "libQt6QuickTimelineBlendTrees",
+        "libQt6QuickVectorImage",
+        "libQt6QuickVectorImageGenerator"
     ]
 
-    # Adding GraphicalEffects files
-    data.qml.append("Qt5Compat/GraphicalEffects")
-
     data.qtlib.extend(_qtlib)
     data.metatypes.extend(_metatypes)
     json_data = get_module_json_data("Quick")
     data.plugins = get_module_plugins(json_data)
+    data.extra_files.append("svgtoqml*")
 
     return data
 
@@ -589,6 +604,9 @@ def module_QtQuickControls2() -> ModuleData:
     data.qtlib.append("libQt6QuickControls2MaterialStyleImpl")
     data.qtlib.append("libQt6QuickControls2Universal")
     data.qtlib.append("libQt6QuickControls2UniversalStyleImpl")
+    # FluentWinUI3 Style is available for all platforms, even if it
+    # was originally intended for Windows.
+    data.qtlib.append("libQt6QuickControls2FluentWinUI3StyleImpl")
     if sys.platform == "win32":
         data.qtlib.append("libQt6QuickControls2WindowsStyleImpl")
     elif sys.platform == "darwin":
@@ -747,6 +765,7 @@ def module_QtQuick3D() -> ModuleData:
         "libQt63DQuickInput",
         "libQt63DQuickRender",
         "libQt63DQuickScene2D",
+        "libQt6Quick3DXr",
     ]
 
     _metatypes = [
@@ -767,6 +786,7 @@ def module_QtQuick3D() -> ModuleData:
         "qt6quick3druntimerender_relwithdebinfo_metatypes.json",
         "qt6quick3dutils_relwithdebinfo_metatypes.json",
         "qt6shadertools_relwithdebinfo_metatypes.json",
+        "qt6quick3dxr_relwithdebinfo_metatypes.json"
     ]
 
     json_data = get_module_json_data("Quick3DAssetImport")
@@ -777,6 +797,9 @@ def module_QtQuick3D() -> ModuleData:
     data.extra_files.append("qsb*")
     data.extra_files.append("balsam*")
 
+    # Adding GraphicalEffects files
+    data.qml.append("Qt5Compat/GraphicalEffects")
+
     return data
 
 
@@ -841,6 +864,12 @@ def module_QtGraphs() -> ModuleData:
     return data
 
 
+def module_QtGraphsWidgets() -> ModuleData:
+    data = ModuleData("GraphsWidgets")
+
+    return data
+
+
 def module_QtMultimedia() -> ModuleData:
     data = ModuleData("Multimedia")
     data.qtlib.append("libQt6MultimediaQuick")
@@ -850,9 +879,14 @@ def module_QtMultimedia() -> ModuleData:
     data.translations.append("qtmultimedia_*")
     data.plugins = get_module_plugins(json_data)
 
-    if sys.platform == "win32":
-        data.extra_files.extend(["avcodec-60.dll", "avformat-60.dll", "avutil-58.dll",
-                                 "swresample-4.dll", "swscale-7.dll"])
+    linux_multimedia_libs = [f"Qt/lib/lib{lib}*.so*" for lib in _multimedia_libs]
+    linux_multimedia_libs.append("Qt/lib/libQt6FFmpegStub*.so*")
+    platform_files = {
+        "win32": [f"{lib}-*.dll" for lib in _multimedia_libs],
+        "darwin": [f"Qt/lib/lib{lib}.*.dylib" for lib in _multimedia_libs],
+        "linux": linux_multimedia_libs}
+    extra_files = platform_files.get(sys.platform, [])
+    data.extra_files.extend(extra_files)
 
     return data
 
@@ -1036,3 +1070,15 @@ def module_QtAsyncio() -> ModuleData:
 def module_QtExampleIcons() -> ModuleData:
     data = ModuleData("ExampleIcons")
     return data
+
+
+def module_QtWebView() -> ModuleData:
+    data = ModuleData("WebView")
+    json_data = get_module_json_data("WebView")
+    data.plugins = get_module_plugins(json_data)
+    return data
+
+
+def module_QtWebViewQuick() -> ModuleData:
+    data = ModuleData("WebViewQuick")
+    return data
index f3f9f17a926f5c79a21cea4f37b198b97b51afcd..b389bb87d14b7516a9d00cb1479db7ad21c544b4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 
 import platform
@@ -47,7 +48,7 @@ class PysideBuildWheel(_bdist_wheel, CommandMixin):
 
     def finalize_options(self):
         CommandMixin.mixin_finalize_options(self)
-        if sys.platform == 'darwin':
+        if sys.platform == 'darwin' and not self.is_cross_compile:
             # Override the platform name to contain the correct
             # minimum deployment target.
             # This is used in the final wheel name.
index 5ec26c742626ed1d0aa06711d5940e06239284ab..aa4ef02cd315befa2972491fc69e6df558605fa2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import time
 from pathlib import Path
index c03b1b8ae002a0448874f1d27a83cfe0538fb460..478bdfdf717eef310154aa28da27d10b83403b21 100644 (file)
@@ -1,6 +1,6 @@
 product_dependency:
   ../../qt/qt5:
-    ref: "3f005f1e2e88485dbf541200ba3fafcad6ea84ad"
+    ref: "480041bb0bfd400f29dd49facfaa924dacac5c03"
 dependency_source: supermodule
 dependencies: [
       "../../qt/qt3d",
@@ -36,5 +36,6 @@ dependencies: [
       "../../qt/qtwayland",
       "../../qt/qtwebchannel",
       "../../qt/qtwebengine",
-      "../../qt/qtwebsockets"
+      "../../qt/qtwebsockets",
+      "../../qt/qtwebview",
       ]
diff --git a/coin/fetch_libclang_arm64.sh b/coin/fetch_libclang_arm64.sh
new file mode 100644 (file)
index 0000000..c99e335
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# Download the file
+wget -q https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.7-based-linux-Debian-11.6-gcc10.2-arm64.7z
+if [ $? -ne 0 ]; then
+    echo "Error: Failed to download libclang archive" >&2
+    exit 1
+fi
+
+# Unzip the contents to /home/qt
+7z x libclang-release_18.1.7-based-linux-Debian-11.6-gcc10.2-arm64.7z -o/home/qt
+if [ $? -ne 0 ]; then
+    echo "Error: Failed to extract libclang archive" >&2
+    exit 1
+fi
+
+# Remove the 7z file after extraction
+rm libclang-release_18.1.7-based-linux-Debian-11.6-gcc10.2-arm64.7z
+if [ $? -ne 0 ]; then
+    echo "Error: Failed to remove libclang archive" >&2
+    exit 1
+fi
index 441a018032116622444bb42eb65302c1db67b8a4..7663014e2368cb67fc4fbf890d8b654747fb7f38 100644 (file)
@@ -140,8 +140,8 @@ instructions:
       variableValue: "/Users/qt/.local/bin/:"
       enable_if:
          condition: property
-         property: host.osVersion
-         equals_value: MacOS_11_00
+         property: host.os
+         equals_value: MacOS
     - type: PrependToEnvironmentVariable
       variableName: PATH
       variableValue: "/Users/qt/work/install/bin:"
@@ -191,20 +191,8 @@ instructions:
          condition: property
          property: target.compiler
          equals_value: ICC_18
-    - type: EnvironmentVariable
-      variableName: LLVM_INSTALL_DIR
-      variableValue: "{{.Env.LLVM_DYNAMIC_LIBS_100}}"
-      disable_if:
-        condition: and
-        conditions:
-          - condition: property
-            property: target.arch
-            equals_value: X86_64-ARM64 # When target arch is universal binary, we can use the default libclang
-          - condition: property
-            property: host.os
-            equals_value: MacOS
     - type: ExecuteCommand
-      command: "sudo apt-get install python3-pip libclang-11-dev clang -y"
+      command: "sudo apt-get install python3-pip -y"
       maxTimeInSeconds: 14400
       maxTimeBetweenOutput: 1200
       enable_if:
@@ -219,7 +207,7 @@ instructions:
       userMessageOnFailure: >
           Failed to install dependencies
     - type: ExecuteCommand
-      command: "python3 -m pip install -U setuptools==69.1.1"
+      command: "chmod +x coin/fetch_libclang_arm64.sh"
       maxTimeInSeconds: 14400
       maxTimeBetweenOutput: 1200
       enable_if:
@@ -232,10 +220,25 @@ instructions:
               property: host.arch
               equals_value: AARCH64
       userMessageOnFailure: >
-          Failed to install setuptools
+          Failed to make coin/fetch_libclang_arm64.sh executable
+    - type: ExecuteCommand
+      command: "coin/fetch_libclang_arm64.sh"
+      maxTimeInSeconds: 14400
+      maxTimeBetweenOutput: 1200
+      enable_if:
+          condition: and
+          conditions:
+            - condition: property
+              property: host.os
+              equals_value: Linux
+            - condition: property
+              property: host.arch
+              equals_value: AARCH64
+      userMessageOnFailure: >
+          Failed to download libclang from Qt servers
     - type: EnvironmentVariable
       variableName: LLVM_INSTALL_DIR
-      variableValue: "/usr/lib/llvm-11/lib"
+      variableValue: "/home/qt/libclang"
       enable_if:
         condition: and
         conditions:
@@ -251,11 +254,69 @@ instructions:
       enable_if:
          condition: property
          property: host.osVersion
-         in_values: [RHEL_8_6, RHEL_8_8]
+         in_values: [RHEL_8_6, RHEL_8_8, RHEL_8_10]
     - type: EnvironmentVariable
       variableName: interpreter
       variableValue: "python3"
       enable_if: # Enable for others than rhl 8.*
         condition: property
-        property: target.osVersion
-        not_in_values: [RHEL_8_6, RHEL_8_8]
+        property: host.osVersion
+        not_in_values: [RHEL_8_6, RHEL_8_8, RHEL_8_10]
+    # ToDo: can be removed after 3.11 is available on qt5#3.8 on macOS
+    # start of ToDo
+    - type: ExecuteCommand
+      command: "tools/install-p311.sh"
+      maxTimeInSeconds: 14400
+      maxTimeBetweenOutput: 1200
+      ignoreExitCode: true
+      enable_if:
+          condition: and
+          conditions:
+          - condition: property
+            property: host.os
+            equals_value: MacOS
+          - condition: property
+            property: target.os
+            equals_value: Android
+      userMessageOnFailure: >
+          Failed to install python 3.11
+    - type: PrependToEnvironmentVariable
+      variableName: PATH
+      variableValue: "/Users/qt/python311/bin:"
+      enable_if:
+          condition: and
+          conditions:
+          - condition: property
+            property: host.os
+            equals_value: MacOS
+          - condition: property
+            property: target.os
+            equals_value: Android
+    # end of ToDo
+    - type: ExecuteCommand
+      command: "virtualenv -p {{.Env.interpreter}} {{.AgentWorkingDir}}/env"
+      maxTimeInSeconds: 14400
+      maxTimeBetweenOutput: 1200
+      enable_if:
+          condition: or
+          conditions:
+          - condition: property
+            property: host.os
+            equals_value: Linux
+          - condition: property
+            property: host.os
+            equals_value: MacOS
+      userMessageOnFailure: >
+          Failed to create env
+    - type: EnvironmentVariable
+      variableName: ENV_PYTHON
+      variableValue: "{{.AgentWorkingDir}}/env/bin/python"
+    - type: AppendToEnvironmentVariable
+      variableName: PATH
+      variableValue: ":{{.AgentWorkingDir}}/env/bin/"
+      enable_if: # Enable for others than rhl 8.*
+        condition: property
+        property: target.os
+        equals_value: Android
+
+
diff --git a/coin/instructions/execute_android_instructions.yaml b/coin/instructions/execute_android_instructions.yaml
new file mode 100644 (file)
index 0000000..ab4d029
--- /dev/null
@@ -0,0 +1,76 @@
+type: Group
+instructions:
+  - type: ExecuteCommand
+    command: "{{.Env.interpreter}} -m pip install --user -r requirements.txt"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    enable_if:
+        condition: or
+        conditions:
+        - condition: property
+          property: host.os
+          equals_value: Linux
+        - condition: property
+          property: host.os
+          equals_value: MacOS
+    userMessageOnFailure: >
+         Failed to install deps
+  - type: ExecuteCommand
+    command: "{{.Env.interpreter}} -m pip install --user -r tools/cross_compile_android/requirements.txt"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    ignoreExitCode: true
+    enable_if:
+        condition: or
+        conditions:
+        - condition: property
+          property: host.os
+          equals_value: Linux
+        - condition: property
+          property: host.os
+          equals_value: MacOS
+    userMessageOnFailure: >
+         Failed to install deps
+  - type: ExecuteCommand
+    command: "{{.Env.interpreter}} tools/cross_compile_android/main.py --qt-install-path /Users/qt/work/install --auto-accept-license --skip-update --verbose --ndk-path {{.Env.ANDROID_NDK_ROOT_DEFAULT}} --sdk-path {{.Env.ANDROID_SDK_ROOT}} --plat-name aarch64 --coin"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    ignoreExitCode: true
+    enable_if:
+      condition: property
+      property: host.os
+      equals_value: MacOS
+    userMessageOnFailure: >
+         Failed to execute build instructions on macOS
+  - type: ExecuteCommand
+    command: "{{.Env.interpreter}} tools/cross_compile_android/main.py --qt-install-path /home/qt/work/install --auto-accept-license --skip-update --verbose --ndk-path {{.Env.ANDROID_NDK_ROOT_DEFAULT}} --sdk-path {{.Env.ANDROID_SDK_ROOT}} --plat-name x86_64 --coin"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    ignoreExitCode: true
+    enable_if:
+        condition: and
+        conditions:
+        - condition: property
+          property: host.os
+          equals_value: Linux
+        - condition: property
+          property: target.arch
+          equals_value: X86_64
+    userMessageOnFailure: >
+         Failed to execute build instructions on Linux
+  # ToDo: Do we need x86 build?
+  # - type: ExecuteCommand
+  #   command: "{{.Env.interpreter}} tools/cross_compile_android/main.py --qt-install-path /home/qt/work/install --auto-accept-license --skip-update --verbose --ndk-path {{.Env.ANDROID_NDK_ROOT_DEFAULT}} --sdk-path {{.Env.ANDROID_SDK_ROOT}} --plat-name x86 --clean-cache all --coin"
+  #   maxTimeInSeconds: 14400
+  #   maxTimeBetweenOutput: 1200
+  #   enable_if:
+  #       condition: and
+  #       conditions:
+  #       - condition: property
+  #         property: host.os
+  #         equals_value: Linux
+  #       - condition: property
+  #         property: target.arch
+  #         equals_value: X86
+  #   userMessageOnFailure: >
+  #        Failed to execute build instructions on Linux
index 317adae2a1eb7879d2c4a5dfd5e01a2ef65079ec..310f00e9428e2d9261a8fac64e0e3d7e23ae5648 100644 (file)
 type: Group
 instructions:
-  - type: ExecuteCommand
-    command: "python3 -m pip install -U setuptools==69.1.1"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 1200
+  - type: Group
+    instructions:
+      - !include "{{pyside/pyside-setup}}/execute_android_instructions.yaml"
     enable_if:
-        condition: property
-        property: host.os
-        equals_value: MacOS
-    userMessageOnFailure: >
-         Failed to install setuptools on macOS
-  - type: ExecuteCommand
-    command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install  --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=ALL"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 1200
-    enable_if:
-        condition: and
-        conditions:
-        - condition: property
-          property: target.arch
-          equals_value: X86_64
-        - condition: property
-          property: host.os
-          equals_value: MacOS
-    userMessageOnFailure: >
-         Failed to execute build instructions on macOS
-  - type: ExecuteCommand
-    command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install  --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch=X86_64-ARM64 --phase=ALL"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 1200
+      condition: property
+      property: target.os
+      equals_value: Android
+  - type: Group
+    instructions:
+      - !include "{{pyside/pyside-setup}}/execute_desktop_instructions.yaml"
     enable_if:
         condition: and
         conditions:
         - condition: property
-          property: target.arch
-          equals_value: X86_64-ARM64
-        - condition: property
-          property: host.os
-          equals_value: MacOS
-    userMessageOnFailure: >
-         Failed to execute build instructions on macOS
-  - type: EnvironmentVariable
-    variableName: PYSIDE_SIGNING_DIR
-    variableValue: "{{.AgentWorkingDir}}\\pyside\\{{.Env.TESTED_MODULE_COIN}}\\build\\qfpa-p3.10\\package_for_wheels"
-    enable_if:
-        condition: property
-        property: host.os
-        equals_value: Windows
-  - type: ExecuteCommand
-    command: "{{.Env.interpreter}} -m pip install -U pip setuptools==69.1.1 --user"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 1200
-    enable_if:
-        condition: property
-        property: host.os
-        equals_value: Linux
-    userMessageOnFailure: >
-         Failed to install setuptools on Linux
-  - type: ExecuteCommand
-    command: "{{.Env.interpreter}} -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/home/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch={{.Env.HOST_ARCH_COIN}} --targetArch={{.Env.TARGET_ARCH_COIN}}--phase=ALL"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 600
-    enable_if:
-        condition: property
-        property: host.os
-        equals_value: Linux
-    userMessageOnFailure: >
-         Failed to execute build instructions on Linux
-  - type: ExecuteCommand
-    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U setuptools==69.1.1"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 1200
-    enable_if:
-        condition: property
-        property: host.os
-        equals_value: Windows
-    userMessageOnFailure: >
-         Failed to install setuptools on Windows
-  - type: ExecuteCommand
-    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=\\Users\\qt\\work\\install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=BUILD"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 600
-    enable_if:
-        condition: property
-        property: host.os
-        equals_value: Windows
-    userMessageOnFailure: >
-         Failed to execute build instructions on Windows
-  - type: SignPackage
-    directory: "{{.Env.PYSIDE_SIGNING_DIR}}"
-    maxTimeInSeconds: 1200
-    maxTimeBetweenOutput: 1200
-    enable_if:
-      condition: property
-      property: host.os
-      equals_value: Windows
-  - type: ChangeDirectory
-    directory: "{{.AgentWorkingDir}}\\pyside\\pyside-setup"
-    enable_if:
-        condition: property
-        property: host.os
-        equals_value: Windows
-  - type: ExecuteCommand
-    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYSIDE_VIRTUALENV}}\\Scripts\\python.exe create_wheels.py"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 300
-    enable_if:
-        condition: property
-        property: host.os
-        equals_value: Windows
-    userMessageOnFailure: >
-         Failed to create wheels for Windows
-  - type: ExecuteCommand
-    command: "{{.Env.PYSIDE_VIRTUALENV}}/bin/python create_wheels.py"
-    maxTimeInSeconds: 14400
-    maxTimeBetweenOutput: 300
-    enable_if:
-      condition: or
-      conditions:
-        - condition: property
-          property: host.os
-          equals_value: MacOS
+          property: host.compiler
+          not_equals_value: Mingw
         - condition: property
-          property: host.os
-          equals_value: Linux
-    userMessageOnFailure: >
-         Failed to create wheels
+          property: target.os
+          not_equals_value: Android
diff --git a/coin/instructions/execute_desktop_instructions.yaml b/coin/instructions/execute_desktop_instructions.yaml
new file mode 100644 (file)
index 0000000..bece46b
--- /dev/null
@@ -0,0 +1,133 @@
+type: Group
+instructions:
+  - type: ExecuteCommand
+    command: "python3 -m pip install -r requirements-coin.txt"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    enable_if:
+        condition: property
+        property: host.os
+        equals_value: MacOS
+    userMessageOnFailure: >
+         Failed to install requirements-coin.txt dependencies on macOS
+  - type: ExecuteCommand
+    command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install  --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=ALL"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    enable_if:
+        condition: and
+        conditions:
+        - condition: property
+          property: target.arch
+          equals_value: X86_64
+        - condition: property
+          property: host.os
+          equals_value: MacOS
+    userMessageOnFailure: >
+         Failed to execute build instructions on macOS
+  - type: ExecuteCommand
+    command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install  --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch=X86_64-ARM64 --phase=ALL"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    enable_if:
+        condition: and
+        conditions:
+        - condition: property
+          property: target.arch
+          equals_value: X86_64-ARM64
+        - condition: property
+          property: host.os
+          equals_value: MacOS
+    userMessageOnFailure: >
+         Failed to execute build instructions on macOS
+  - type: EnvironmentVariable
+    variableName: PYSIDE_SIGNING_DIR
+    variableValue: "{{.AgentWorkingDir}}\\pyside\\{{.Env.TESTED_MODULE_COIN}}\\build\\qfpa-p3.10\\package_for_wheels"
+    enable_if:
+        condition: property
+        property: host.os
+        equals_value: Windows
+  - type: ExecuteCommand
+    command: "{{.Env.interpreter}} -m pip install -r requirements-coin.txt --user"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    enable_if:
+        condition: and
+        conditions:
+        - condition: property
+          property: host.os
+          equals_value: Linux
+        - condition: property
+          property: host.osVersion
+          not_equals_value: Ubuntu_24_04
+    userMessageOnFailure: >
+         Failed to install requirements-coin.txt dependencies on Linux
+  - type: ExecuteCommand
+    command: "{{.Env.interpreter}} -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/home/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch={{.Env.HOST_ARCH_COIN}} --targetArch={{.Env.TARGET_ARCH_COIN}}--phase=ALL"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 600
+    enable_if:
+        condition: property
+        property: host.os
+        equals_value: Linux
+    userMessageOnFailure: >
+         Failed to execute build instructions on Linux
+  - type: ExecuteCommand
+    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -r requirements-coin.txt"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 1200
+    enable_if:
+        condition: property
+        property: host.os
+        equals_value: Windows
+    userMessageOnFailure: >
+         Failed to install requirements-coin.txt dependencies on Windows
+  - type: ExecuteCommand
+    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=\\Users\\qt\\work\\install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=BUILD"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 600
+    enable_if:
+        condition: property
+        property: host.os
+        equals_value: Windows
+    userMessageOnFailure: >
+         Failed to execute build instructions on Windows
+  - type: SignPackage
+    directory: "{{.Env.PYSIDE_SIGNING_DIR}}"
+    maxTimeInSeconds: 1200
+    maxTimeBetweenOutput: 1200
+    enable_if:
+      condition: property
+      property: host.os
+      equals_value: Windows
+  - type: ChangeDirectory
+    directory: "{{.AgentWorkingDir}}\\pyside\\pyside-setup"
+    enable_if:
+        condition: property
+        property: host.os
+        equals_value: Windows
+  - type: ExecuteCommand
+    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYSIDE_VIRTUALENV}}\\Scripts\\python.exe create_wheels.py"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 300
+    enable_if:
+        condition: property
+        property: host.os
+        equals_value: Windows
+    userMessageOnFailure: >
+         Failed to create wheels for Windows
+  - type: ExecuteCommand
+    command: "{{.Env.PYSIDE_VIRTUALENV}}/bin/python create_wheels.py"
+    maxTimeInSeconds: 14400
+    maxTimeBetweenOutput: 300
+    enable_if:
+      condition: or
+      conditions:
+        - condition: property
+          property: host.os
+          equals_value: MacOS
+        - condition: property
+          property: host.os
+          equals_value: Linux
+    userMessageOnFailure: >
+         Failed to create wheels
diff --git a/coin/instructions/execute_license_check.yaml b/coin/instructions/execute_license_check.yaml
deleted file mode 100644 (file)
index 0bf4d3c..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-type: Group
-instructions:
-    - type: ChangeDirectory
-      directory: "{{.AgentWorkingDir}}"
-    - type: InstallSourceArchive
-      maxTimeInSeconds: 600
-      maxTimeBetweenOutput: 600
-      project: qt/qtbase
-      ref: "CURRENT_BRANCH"
-      directory: qt/qtbase
-      userMessageOnFailure: >
-              Could not install source archive. Please investigate why.
-    - type: InstallSourceArchive
-      maxTimeInSeconds: 600
-      maxTimeBetweenOutput: 600
-      project: qt/qtqa
-      ref: master
-      directory: qt/qtqa-latest
-      userMessageOnFailure: >
-              Could not install source archive. Please investigate why.
-    - type: EnvironmentVariable
-      variableName: QT_MODULE_TO_TEST
-      variableValue: pyside/pyside-setup
-      userMessageOnFailure: >
-              Failed to set environment variable. This should not happen.
-    - type: ExecuteCommand
-      command: perl qt/qtqa-latest/tests/prebuild/license/tst_licenses.pl
-      maxTimeInSeconds: 7200
-      maxTimeBetweenOutput: 120
-      userMessageOnFailure: >
-           Failed to run license check script.
index 780b27ec7ddb821205bafc5baf4d2e95548d3dff..7399ccb42c18745f169974090cddf4d9a7793f8b 100644 (file)
@@ -5,7 +5,7 @@ enable_if:
   not_contains_value: LicenseCheck
 instructions:
   - type: ExecuteCommand
-    command: "python3 -m pip install -U setuptools==69.1.1"
+    command: "python3 -m pip install -r requirements-coin.txt"
     maxTimeInSeconds: 14400
     maxTimeBetweenOutput: 1200
     enable_if:
@@ -13,7 +13,7 @@ instructions:
         property: host.os
         equals_value: MacOS
     userMessageOnFailure: >
-         Failed to install setuptools on macOS
+         Failed to install requirements-coin.txt on macOS
   - type: ExecuteCommand
     command: "python3 -u coin_test_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} --instdir=/Users/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=ARM64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}}"
     maxTimeInSeconds: 14400
@@ -22,11 +22,14 @@ instructions:
         condition: and
         conditions:
           - condition: property
-            property: host.osVersion
-            equals_value: MacOS_11_00
+            property: host.os
+            equals_value: MacOS
           - condition: property
             property: host.arch
             equals_value: ARM64
+          - condition: property
+            property: target.os
+            not_equals_value: Android
     userMessageOnFailure: >
          Failed to execute test instructions on arm mac
   - type: ExecuteCommand
@@ -42,10 +45,13 @@ instructions:
           - condition: property
             property: host.arch
             equals_value: X86_64
+          - condition: property
+            property: target.os
+            not_equals_value: Android
     userMessageOnFailure: >
          Failed to execute test instructions on macOS
   - type: ExecuteCommand
-    command: "{{.Env.interpreter}} -m pip install -U pip setuptools==69.1.1 --user"
+    command: "{{.Env.interpreter}} -m pip install -r requirements-coin.txt --user"
     maxTimeInSeconds: 14400
     maxTimeBetweenOutput: 1200
     enable_if:
@@ -53,19 +59,24 @@ instructions:
         property: host.os
         equals_value: Linux
     userMessageOnFailure: >
-         Failed to install setuptools on Linux
+         Failed to install requirements-coin.txt on Linux
   - type: ExecuteCommand
     command: "{{.Env.interpreter}} -u coin_test_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} --instdir=/home/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}}"
     maxTimeInSeconds: 14400
     maxTimeBetweenOutput: 600
     enable_if:
-        condition: property
-        property: host.os
-        equals_value: Linux
+        condition: and
+        conditions:
+          - condition: property
+            property: host.os
+            equals_value: Linux
+          - condition: property
+            property: target.os
+            not_equals_value: Android
     userMessageOnFailure: >
          Failed to execute test instructions on Linux
   - type: ExecuteCommand
-    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U pip setuptools==69.1.1 --user"
+    command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -r requirements-coin.txt --user"
     maxTimeInSeconds: 14400
     maxTimeBetweenOutput: 1200
     enable_if:
@@ -73,14 +84,22 @@ instructions:
         property: host.os
         equals_value: Windows
     userMessageOnFailure: >
-         Failed to install setuptools on Windows
+         Failed to install requirements-coin.txt on Windows
   - type: ExecuteCommand
     command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -u coin_test_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} --instdir=c:\\Users\\qt\\work\\install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}}"
     maxTimeInSeconds: 14400
     maxTimeBetweenOutput: 600
     enable_if:
-        condition: property
-        property: host.os
-        equals_value: Windows
+        condition: and
+        conditions:
+          - condition: property
+            property: host.os
+            equals_value: Windows
+          - condition: property
+            property: target.os
+            not_equals_value: Android
+          - condition: property
+            property: host.compiler
+            not_equals_value: Mingw
     userMessageOnFailure: >
          Failed to execute test instructions on 64 bit Windows
diff --git a/coin/instructions_utils.py b/coin/instructions_utils.py
new file mode 100644 (file)
index 0000000..176a6d2
--- /dev/null
@@ -0,0 +1,262 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import calendar
+import datetime
+import os
+import site
+import sys
+from pathlib import Path
+
+from build_scripts.options import Options
+from build_scripts.utils import (parse_cmake_conf_assignments_by_key,
+                                 remove_tree, run_instruction)
+
+options = Options()
+
+
+class CI:
+    def __init__(self):
+        # Values must match COIN thrift
+        self.HOST_OS = options.option_value("os")
+        self.TARGET_OS = options.option_value("targetOs")
+        self.HOST_ARCH = options.option_value("hostArch")
+        self.TARGET_ARCH = options.option_value("targetArch")
+        self.HOST_OS_VER = options.option_value("osVer")
+        self.ENV_INSTALL_DIR = options.option_value("instdir")
+        self.ENV_AGENT_DIR = options.option_value("agentdir") or "."
+        self.COMPILER = options.option_value("compiler")
+        self.USE_SCCACHE = options.option_value("compiler-launcher")
+        self.INTEGRATION_ID = options.option_value("coinIntegrationId") or str(
+            calendar.timegm(datetime.datetime.now().timetuple())
+        )
+        self.FEATURES = []
+        _ci_features = options.option_value("features")
+        if _ci_features is not None:
+            for f in _ci_features.split(", "):
+                self.FEATURES.append(f)
+        self.RELEASE_CONF = options.has_option("packaging")
+        self.TEST_PHASE = options.option_value("phase")
+        if self.TEST_PHASE not in ["ALL", "BUILD"]:
+            self.TEST_PHASE = "ALL"
+
+
+def get_ci_exe_path(ci_install_dir, ci_host_os, qtexe):
+    """
+    qtexe can only be 'qmake' or 'qtpaths'
+    """
+    ext = ""
+    if ci_host_os == "Windows":
+        ext = ".exe"
+
+    _path = Path(ci_install_dir) / "bin" / f"{qtexe}{ext}"
+
+    return f"--{qtexe}={_path}"
+
+
+def get_env_or_raise(name: str) -> str:
+    o = os.getenv(name)
+    if o is None:
+        raise Exception(f"Variable not defined: {name}")
+    return o
+
+
+def get_qtci_virtualenv(python_ver, log, host, host_arch, target_arch):
+    _exe = "python"
+    _env = os.environ.get("PYSIDE_VIRTUALENV") or f"env{python_ver}"
+    env_python = f"{_env}/bin/python"
+    env_pip = f"{_env}/bin/pip"
+
+    if host == "Windows":
+        log.info("New virtualenv to build {target_arch} in {host_arch} host")
+        _exe = "python.exe"
+        if python_ver.startswith("3"):
+            var = f"PYTHON{python_ver}-64_PATH"
+            log.info(f"Try to find python from {var} env variable")
+            _path = Path(os.getenv(var, ""))
+            _exe = _path / "python.exe"
+            if not _exe.is_file():
+                log.warning(f"Can't find python.exe from {_exe}, using default python3")
+                _exe = Path(get_env_or_raise("PYTHON3_PATH")) / "python.exe"
+        env_python = rf"{_env}\Scripts\python.exe"
+        env_pip = rf"{_env}\Scripts\pip.exe"
+    else:
+        _exe = f"python{python_ver}"
+        try:
+            run_instruction([_exe, "--version"], f"Failed to guess python version {_exe}")
+        except Exception as e:
+            print(f"Exception {type(e).__name__}: {e}")
+            _exe = "python3"
+    return (_exe, _env, env_pip, env_python)
+
+
+def get_current_script_path():
+    """Returns the absolute path containing this script."""
+    try:
+        this_file = __file__
+    except NameError:
+        this_file = sys.argv[0]
+    this_file = Path(this_file).resolve()
+    return this_file.parents[0]
+
+
+def is_snapshot_build():
+    """
+    Returns True if project needs to be built with --snapshot-build
+
+    This is true if the version found in .cmake.conf is not a
+    pre-release version (no alphas, betas).
+
+    This eliminates the need to remove the --snapshot-build option
+    on a per-release branch basis (less things to remember to do
+    for a release).
+    """
+    # This returns pyside-setup/coin/ so we go one level down
+    # to get the root of the repo
+    setup_script_dir = get_current_script_path()
+    pyside_project_dir = setup_script_dir / ".." / "sources" / "pyside6"
+
+    d = parse_cmake_conf_assignments_by_key(str(pyside_project_dir))
+    release_version_type = d.get("pyside_PRE_RELEASE_VERSION_TYPE")
+    pre_release_version = d.get("pyside_PRE_RELEASE_VERSION")
+    if pre_release_version and release_version_type:
+        return True
+    return False
+
+
+def get_architecture(ci):
+    return "32" if ci.TARGET_ARCH == "X86" else "64"
+
+
+def get_python_version(ci):
+    python_ver = "3"
+    if ci.TARGET_OS == "Linux" and ci.HOST_ARCH != "aarch64":
+        python_ver = "3.11"
+    elif ci.TARGET_OS == "Windows":
+        python_ver = "3.10.0"
+    return python_ver
+
+
+def remove_variables(vars):
+    for env_var in vars:
+        if os.environ.get(env_var):
+            del os.environ[env_var]
+
+
+def setup_virtualenv(python, exe, env, pip, log):
+    # Within Ubuntu 24.04 one can't install anything with pip to outside of
+    # virtual env. Trust that we already have proper virtualenv installed.
+    if os.environ.get("HOST_OSVERSION_COIN") != "ubuntu_24_04":
+        run_instruction(
+            [str(python), "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
+            "Failed to pin virtualenv",
+        )
+    # installing to user base might not be in PATH by default.
+    env_path = Path(str(site.USER_BASE)) / "bin"
+    v_env = env_path / "virtualenv"
+    if sys.platform == "win32":
+        env_path = os.path.join(site.USER_BASE, "Scripts")
+        v_env = os.path.join(env_path, "virtualenv.exe")
+    try:
+        run_instruction([str(v_env), "--version"], "Using default virtualenv")
+    except Exception as e:
+        log.info("Failed to use the default virtualenv")
+        log.info(f"{type(e).__name__}: {e}")
+        v_env = "virtualenv"
+    run_instruction([str(v_env), "-p", str(exe), str(env)], "Failed to create virtualenv")
+    # Pip is always upgraded when CI template is provisioned,
+    # upgrading it in later phase may cause perm issue
+    run_instruction(
+        [str(pip), "install", "-r", "requirements.txt"], "Failed to install dependencies"
+    )
+
+
+def call_setup(python_ver, ci, phase, log, buildnro=0):
+    print("call_setup")
+    print("python_ver", python_ver)
+    print("phase", phase)
+    exe, env, pip, env_python = get_qtci_virtualenv(
+        python_ver, log, ci.HOST_OS, ci.HOST_ARCH, ci.TARGET_ARCH
+    )
+
+    if phase not in ["BUILD", "TEST"]:
+        sys.exit(1)
+
+    remove_tree(env, True)
+    # Pinning the virtualenv before creating one
+    # Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
+    python = "python3"
+    if sys.platform == "win32":
+        python = Path(get_env_or_raise("PYTHON3_PATH")) / "python.exe"
+
+    if phase == "BUILD":
+        setup_virtualenv(python, exe, env, pip, log)
+    elif phase == "TEST":
+
+        if ci.HOST_OS == "MacOS" and ci.HOST_ARCH == "ARM64":
+            v_env = "virtualenv"
+            run_instruction([str(v_env), "-p", str(exe), str(env)], "Failed to create virtualenv")
+            run_instruction(
+                [pip, "install", "-r", "requirements.txt"], "Failed to install dependencies"
+            )
+        else:
+            setup_virtualenv(python, exe, env, pip, log)
+            # Install distro to replace missing platform.linux_distribution() in python3.8
+            run_instruction([pip, "install", "distro"], "Failed to install distro")
+
+    if phase == "BUILD":
+        cmd = [
+            env_python,
+            "-u",
+            "setup.py",
+            "build",
+            "--standalone",
+            "--unity",
+            "--build-tests",
+            "--log-level=verbose",
+            "--limited-api=yes",
+        ]
+
+        if ci.TARGET_ARCH == "X86_64-ARM64":
+            cmd += ["--macos-arch='x86_64;arm64'"]
+
+        if ci.USE_SCCACHE:
+            cmd += [f"--compiler-launcher={ci.USE_SCCACHE}"]
+
+        if is_snapshot_build():
+            cmd += ["--snapshot-build"]
+
+        qtpaths_path = get_ci_exe_path(ci.ENV_INSTALL_DIR, ci.HOST_OS, "qtpaths")
+        cmd.append(qtpaths_path)
+
+        # Due to certain older CMake versions generating very long paths
+        # (at least with CMake 3.6.2) when using the export() function,
+        # pass the shorter paths option on Windows so we don't hit
+        # the path character length limit (260).
+        if ci.HOST_OS == "Windows":
+            cmd += ["--shorter-paths"]
+
+        cmd += ["--package-timestamp=" + ci.INTEGRATION_ID]
+
+        env = os.environ
+        run_instruction(cmd, "Failed to run setup.py for build", initial_env=env)
+    elif phase == "TEST":
+        cmd = [
+            env_python,
+            "testrunner.py",
+            "test",
+            "--blacklist",
+            "build_history/blacklist.txt",
+            f"--buildno={buildnro}",
+        ]
+        run_instruction(cmd, "Failed to run testrunner.py")
+
+        qmake_path = get_ci_exe_path(ci.ENV_INSTALL_DIR, ci.HOST_OS, "qmake")
+
+        # Try to install built wheels, and build some buildable examples.
+        if ci.RELEASE_CONF:
+            wheel_tester_path = os.path.join("testing", "wheel_tester.py")
+            # Run the test for the new set of wheels
+            cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist", "--new"]
+            run_instruction(cmd, "Error while running wheel_tester.py on new wheels")
index e5c2fdc8c578ee12f3bcf2662a575e2692e56691..450365ca8fa6493f5619f5181663098f1bf1d044 100644 (file)
@@ -51,36 +51,32 @@ accept_configuration:
         - condition: property
           property: target.os
           not_contains_value: IOS
-    - condition: and
-      conditions:
         - condition: property
           property: host.osVersion
-          equals_value: MacOS_11_00
-        - condition: property
-          property: host.arch
-          equals_value: ARM64
-        - condition: property
-          property: features
-          contains_value: TestOnly
-        - condition: property
-          property: features
-          contains_value: Packaging
+          not_equals_value: MacOS_12
     - condition: and # Restore LoA config
       conditions:
         - condition: property
-          property: host.osVersion
-          equals_value: Debian_11_6
+          property: host.os
+          equals_value: Linux
         - condition: property
           property: host.arch
           equals_value: AARCH64
+        - condition: property
+          property: target.arch
+          equals_value: AARCH64
         - condition: property
           property: features
-          not_contains_value: DebianPackaging
+          contains_value: Packaging
+    - condition: and
+      conditions:
+        - condition: property
+          property: target.os
+          equals_value: Android
         - condition: property
           property: features
           contains_value: Packaging
 
-
 machine_type:
   Build:
     cores: 8
@@ -109,7 +105,7 @@ upload_pyside: &upload_pyside
 
 instructions:
      LicenseCheck:
-      - !include "{{pyside/pyside-setup}}/execute_license_check.yaml"
+      - !include "{{qt/qtbase}}/run_license_check.yaml"
      Build:
       - !include "{{pyside/pyside-setup}}/common_environment.yaml"
       - !include "{{pyside/pyside-setup}}/find_path_to_msvc_compiler.yaml"
index 83d46977ee64133bcf4eabc287029623d17bacd3..b5ade8225aa76d4225cf1944754d773c55a0b484 100644 (file)
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-import calendar
-import datetime
+from __future__ import annotations
+
 import logging
 import os
-import os.path
-import site
 import sys
 
 from build_scripts.log import log
-from build_scripts.options import has_option, option_value
-from build_scripts.utils import (expand_clang_variables, get_ci_qtpaths_path,
-                                 get_qtci_virtualEnv,
-                                 parse_cmake_conf_assignments_by_key,
-                                 remove_tree, run_instruction)
-
-log.setLevel(logging.INFO)
-
-# Values must match COIN thrift
-CI_HOST_OS = option_value("os")
-CI_TARGET_OS = option_value("targetOs")
-CI_HOST_ARCH = option_value("hostArch")
-CI_TARGET_ARCH = option_value("targetArch")
-CI_HOST_OS_VER = option_value("osVer")
-CI_ENV_INSTALL_DIR = option_value("instdir")
-CI_ENV_AGENT_DIR = option_value("agentdir")
-CI_COMPILER = option_value("compiler")
-CI_USE_SCCACHE = option_value("compiler-launcher")
-CI_INTEGRATION_ID = option_value("coinIntegrationId") or str(
-    calendar.timegm(datetime.datetime.now().timetuple())
-)
-CI_FEATURES = []
-_ci_features = option_value("features")
-if _ci_features is not None:
-    for f in _ci_features.split(", "):
-        CI_FEATURES.append(f)
-CI_RELEASE_CONF = has_option("packaging")
-CI_TEST_PHASE = option_value("phase")
-if CI_TEST_PHASE not in ["ALL", "BUILD"]:
-    CI_TEST_PHASE = "ALL"
-
-
-def get_current_script_path():
-    """Returns the absolute path containing this script."""
-    try:
-        this_file = __file__
-    except NameError:
-        this_file = sys.argv[0]
-    this_file = os.path.abspath(this_file)
-    return os.path.dirname(this_file)
-
-
-def is_snapshot_build():
-    """
-    Returns True if project needs to be built with --snapshot-build
-
-    This is true if the version found in .cmake.conf is not a
-    pre-release version (no alphas, betas).
-
-    This eliminates the need to remove the --snapshot-build option
-    on a per-release branch basis (less things to remember to do
-    for a release).
-    """
-    setup_script_dir = get_current_script_path()
-    pyside_project_dir = os.path.join(setup_script_dir, "sources", "pyside6")
-
-    d = parse_cmake_conf_assignments_by_key(pyside_project_dir)
-    release_version_type = d.get("pyside_PRE_RELEASE_VERSION_TYPE")
-    pre_release_version = d.get("pyside_PRE_RELEASE_VERSION")
-    if pre_release_version and release_version_type:
-        return True
-    return False
-
-
-def call_setup(python_ver, phase):
-    print("call_setup")
-    print("python_ver", python_ver)
-    print("phase", phase)
-    _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(
-        python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH
-    )
-
-    if phase in ["BUILD"]:
-        remove_tree(_env, True)
-        # Pinning the virtualenv before creating one
-        # Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
-        python3 = "python3"
-        if sys.platform == "win32":
-            python3 = os.path.join(os.getenv("PYTHON3_PATH"), "python.exe")
-        run_instruction(
-            [python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
-            "Failed to pin virtualenv",
-        )
-        # installing to user base might not be in PATH by default.
-        env_path = os.path.join(site.USER_BASE, "bin")
-        v_env = os.path.join(env_path, "virtualenv")
-        if sys.platform == "win32":
-            env_path = os.path.join(site.USER_BASE, "Scripts")
-            v_env = os.path.join(env_path, "virtualenv.exe")
-        try:
-            run_instruction([str(v_env), "--version"], "Using default virtualenv")
-        except Exception as e:
-            log.info("Failed to use the default virtualenv")
-            log.info(f"{type(e).__name__}: {e}")
-            v_env = "virtualenv"
-        run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
-        # When the 'python_ver' variable is empty, we are using Python 2
-        # Pip is always upgraded when CI template is provisioned,
-        # upgrading it in later phase may cause perm issue
-        run_instruction(
-            [str(env_pip), "install", "-r", "requirements.txt"], "Failed to install dependencies"
-        )
+from build_scripts.utils import expand_clang_variables
+from coin.instructions_utils import (CI, call_setup, get_architecture,
+                                     get_python_version, remove_variables)
 
-    cmd = [env_python, "-u", "setup.py"]
-    if phase in ["BUILD"]:
-        cmd += ["build", "--standalone", "--unity"]
-
-    cmd += ["--build-tests", "--log-level=verbose"]
-
-    if CI_TARGET_ARCH == "X86_64-ARM64":
-        cmd += ["--macos-arch='x86_64;arm64'"]
-
-    if CI_USE_SCCACHE:
-        cmd += [f"--compiler-launcher={CI_USE_SCCACHE}"]
-
-    cmd += ["--limited-api=yes"]
-
-    if is_snapshot_build():
-        cmd += ["--snapshot-build"]
-
-    qtpaths_path = get_ci_qtpaths_path(CI_ENV_INSTALL_DIR, CI_HOST_OS)
-    cmd.append(qtpaths_path)
+if __name__ == "__main__":
 
-    # Due to certain older CMake versions generating very long paths
-    # (at least with CMake 3.6.2) when using the export() function,
-    # pass the shorter paths option on Windows so we don't hit
-    # the path character length limit (260).
-    if CI_HOST_OS == "Windows":
-        cmd += ["--shorter-paths"]
+    ci = CI()
+    log.setLevel(logging.INFO)
+    # Remove some environment variables that impact cmake
+    arch = get_architecture(ci)
+    expand_clang_variables(arch)
 
-    cmd += ["--package-timestamp=" + CI_INTEGRATION_ID]
+    remove_variables(["CC", "CXX"])
 
-    env = os.environ
-    run_instruction(cmd, "Failed to run setup.py for build", initial_env=env)
+    python_ver = get_python_version(ci)
 
+    if ci.TEST_PHASE in ["ALL", "BUILD"]:
+        call_setup(python_ver, ci, "BUILD", log)
 
-if __name__ == "__main__":
-    # Remove some environment variables that impact cmake
-    arch = "32" if CI_TARGET_ARCH == "X86" else "64"
-    expand_clang_variables(arch)
-    for env_var in ["CC", "CXX"]:
-        if os.environ.get(env_var):
-            del os.environ[env_var]
-    python_ver = "3"
-    if CI_TARGET_OS in ["Linux"] and CI_HOST_ARCH != "aarch64":
-        python_ver = "3.11"
-    wheel_package_dir = "qfpa-p3.6"
-    if CI_TARGET_OS in ["Windows"]:
-        python_ver = "3.10.0"
-    if CI_TEST_PHASE in ["ALL", "BUILD"]:
-        call_setup(python_ver, "BUILD")
     # Until CI has a feature to set more dynamic signing dir, make sure it actually exist
     if os.environ.get("QTEST_ENVIRONMENT") == "ci" and sys.platform == "win32":
         signing_dir = str(os.environ.get("PYSIDE_SIGNING_DIR"))
index b0e71fba7c8a6030cbbe11abf7957d903c66d975..a6e38b51f699e3ecdedf103773d66761ced46237 100644 (file)
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-import os
+from __future__ import annotations
+
 import logging
-import site
-import sys
+import os
 
 from build_scripts.log import log
-from build_scripts.options import has_option, option_value
-from build_scripts.utils import (expand_clang_variables, get_ci_qmake_path,
-                                 get_qtci_virtualEnv, remove_tree, run_instruction)
-
-log.setLevel(logging.INFO)
-
-# Values must match COIN thrift
-CI_HOST_OS = option_value("os")
-CI_TARGET_OS = option_value("targetOs")
-CI_HOST_ARCH = option_value("hostArch")
-CI_TARGET_ARCH = option_value("targetArch")
-CI_HOST_OS_VER = option_value("osVer")
-CI_ENV_INSTALL_DIR = option_value("instdir")
-CI_ENV_AGENT_DIR = option_value("agentdir") or "."
-CI_COMPILER = option_value("compiler")
-CI_FEATURES = []
-_ci_features = option_value("features")
-if _ci_features is not None:
-    for f in _ci_features.split(', '):
-        CI_FEATURES.append(f)
-CI_RELEASE_CONF = has_option("packaging")
-
-
-def call_testrunner(python_ver, buildnro):
-    _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH,
-                                                           CI_TARGET_ARCH)
-    remove_tree(_env, True)
-    # Pinning the virtualenv before creating one
-    # Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
-    python3 = "python3"
-    if sys.platform == "win32":
-        python3 = os.path.join(os.getenv("PYTHON3_PATH"), "python.exe")
-
-    # we shouldn't install anything outside of virtualenv, while m1 is not virtualized yet
-    if CI_HOST_OS == "MacOS" and CI_HOST_ARCH == "ARM64":
-        v_env = "virtualenv"
-        run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
-        run_instruction([env_pip, "install", "-r", "requirements.txt"],
-                        "Failed to install dependencies")
-    else:
-        run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
-                        "Failed to pin virtualenv")
-        # installing to user base might not be in PATH by default.
-        env_path = os.path.join(site.USER_BASE, "bin")
-        v_env = os.path.join(env_path, "virtualenv")
-        if sys.platform == "win32":
-            env_path = os.path.join(site.USER_BASE, "Scripts")
-            v_env = os.path.join(env_path, "virtualenv.exe")
-        try:
-            run_instruction([str(v_env), "--version"], "Using default virtualenv")
-        except Exception as e:
-            log.info("Failed to use the default virtualenv")
-            log.info(f"{type(e).__name__}: {e}")
-            v_env = "virtualenv"
-        run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
-        # When the 'python_ver' variable is empty, we are using Python 2
-        # Pip is always upgraded when CI template is provisioned,
-        # upgrading it in later phase may cause perm issue
-        run_instruction([env_pip, "install", "-r", "requirements.txt"],
-                        "Failed to install dependencies")
-        # Install distro to replace missing platform.linux_distribution() in python3.8
-        run_instruction([env_pip, "install", "distro"], "Failed to install distro")
+from build_scripts.utils import expand_clang_variables
+from coin.instructions_utils import (CI, call_setup, get_architecture,
+                                     get_python_version, remove_variables)
 
-    cmd = [env_python, "testrunner.py", "test", "--blacklist", "build_history/blacklist.txt",
-           f"--buildno={buildnro}"]
-    run_instruction(cmd, "Failed to run testrunner.py")
-
-    qmake_path = get_ci_qmake_path(CI_ENV_INSTALL_DIR, CI_HOST_OS)
-
-    # Try to install built wheels, and build some buildable examples.
-    if CI_RELEASE_CONF:
-        wheel_tester_path = os.path.join("testing", "wheel_tester.py")
-        # Run the test for the new set of wheels
-        cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist", "--new"]
-        run_instruction(cmd, "Error while running wheel_tester.py on new wheels")
-
-
-def run_test_instructions():
+if __name__ == "__main__":
+    ci = CI()
+    log.setLevel(logging.INFO)
     # Remove some environment variables that impact cmake
-    arch = '32' if CI_TARGET_ARCH == 'X86' else '64'
+    arch = get_architecture(ci)
     expand_clang_variables(arch)
-    for env_var in ['CC', 'CXX']:
-        if os.environ.get(env_var):
-            del os.environ[env_var]
 
-    os.chdir(CI_ENV_AGENT_DIR)
-    testRun = 0
+    remove_variables(["CC", "CXX"])
 
-    # In win machines, there are additional python versions to test with
-    if CI_HOST_OS == "Windows":
-        call_testrunner("3.10.0", str(testRun))
-    elif CI_HOST_OS == "Linux":
-        call_testrunner("3.11", str(testRun))
-    else:
-        call_testrunner("3", str(testRun))
+    python_ver = get_python_version(ci)
 
+    os.chdir(ci.ENV_AGENT_DIR)
+    testRun = 0
 
-if __name__ == "__main__":
-    run_test_instructions()
+    call_setup(python_ver, ci, "TEST", log)
index 063b59c450eb079dbe470fcf6bdc1b47cc01133e..32a89a6a7fbead885b886537f0fb4eca470defb9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import platform
@@ -10,7 +11,6 @@ from argparse import ArgumentParser, Namespace
 from dataclasses import dataclass
 from pathlib import Path
 from shutil import copy, rmtree, copytree
-from typing import List, Optional, Tuple
 
 import build  # type: ignore
 import pyproject_hooks
@@ -29,13 +29,13 @@ PYSIDE_DESCRIPTION = "Python bindings for the Qt cross-platform application and
 @dataclass
 class SetupData:
     name: str
-    version: str
+    version: tuple[str, str]
     description: str
     readme: str
-    console_scripts: List[str]
+    console_scripts: list[str]
 
 
-def get_version_from_package(name: str, package_path: Path) -> str:
+def get_version_from_package(name: str, package_path: Path) -> tuple[str, str]:
     # Get version from the already configured '__init__.py' file
     version = ""
     with open(package_path / name / "__init__.py") as f:
@@ -46,7 +46,7 @@ def get_version_from_package(name: str, package_path: Path) -> str:
     return version, f"{name}.__init__.__version__"
 
 
-def create_module_plugin_json(wheel_name: str, data: List[ModuleData], package_path: Path):
+def create_module_plugin_json(wheel_name: str, data: list[ModuleData], package_path: Path):
     all_plugins = {}
 
     for module in data:
@@ -60,7 +60,7 @@ def create_module_plugin_json(wheel_name: str, data: List[ModuleData], package_p
             json.dump(all_plugins, fp, indent=4)
 
 
-def get_manifest(wheel_name: str, data: List[ModuleData], package_path: Path) -> str:
+def get_manifest(wheel_name: str, data: list[ModuleData], package_path: Path) -> str:
     lines = []
 
     for module in data:
@@ -122,7 +122,11 @@ def get_platform_tag() -> str:
 
         module_name = config_py.name[:-3]
         _spec = importlib.util.spec_from_file_location(f"{module_name}", config_py)
+        if _spec is None:
+            raise RuntimeError(f"Unable to create ModuleSpec from {str(config_py)}")
         _module = importlib.util.module_from_spec(_spec)
+        if _spec.loader is None:
+            raise RuntimeError(f"ModuleSpec for {module_name} has no valid loader.")
         _spec.loader.exec_module(module=_module)
         target = _module.__qt_macos_min_deployment_target__
 
@@ -180,13 +184,13 @@ def generate_pyproject_toml(artifacts: Path, setup: SetupData) -> str:
     with open(artifacts / "pyproject.toml.base") as f:
         content = (
             f.read()
-            .replace("PROJECT_NAME", f'"{setup.name}"')
-            .replace("PROJECT_VERSION", f'"{setup.version[1]}"')
-            .replace("PROJECT_DESCRIPTION", f'"{setup.description}"')
-            .replace("PROJECT_README", f'"{setup.readme}"')
-            .replace("PROJECT_TAG", f'"{_tag}"')
-            .replace("PROJECT_SCRIPTS", _console_scripts)
-            .replace("PROJECT_DEPENDENCIES", f"{_dependencies}")
+            .replace('"PROJECT_NAME"', f'"{setup.name}"')
+            .replace('"PROJECT_VERSION"', f'"{setup.version[1]}"')
+            .replace('"PROJECT_DESCRIPTION"', f'"{setup.description}"')
+            .replace('"PROJECT_README"', f'"{setup.readme}"')
+            .replace('"PROJECT_TAG"', f'"{_tag}"')
+            .replace('# PROJECT_SCRIPTS', _console_scripts)
+            .replace('"PROJECT_DEPENDENCIES"', f"{_dependencies}")
         )
     return content
 
@@ -221,7 +225,7 @@ def generate_setup_py(artifacts: Path, setup: SetupData):
     return content
 
 
-def wheel_shiboken_generator(package_path: Path) -> Tuple[SetupData, None]:
+def wheel_shiboken_generator(package_path: Path) -> tuple[SetupData, None]:
     setup = SetupData(
         name="shiboken6_generator",
         version=get_version_from_package("shiboken6_generator", package_path),
@@ -236,7 +240,7 @@ def wheel_shiboken_generator(package_path: Path) -> Tuple[SetupData, None]:
     return setup, None
 
 
-def wheel_shiboken_module(package_path: Path) -> Tuple[SetupData, None]:
+def wheel_shiboken_module(package_path: Path) -> tuple[SetupData, None]:
     setup = SetupData(
         name="shiboken6",
         version=get_version_from_package("shiboken6", package_path),
@@ -248,15 +252,15 @@ def wheel_shiboken_module(package_path: Path) -> Tuple[SetupData, None]:
     return setup, None
 
 
-def wheel_pyside6_essentials(package_path: Path) -> Tuple[SetupData, List[ModuleData]]:
+def wheel_pyside6_essentials(package_path: Path) -> tuple[SetupData, list[ModuleData]]:
     packaged_qt_tools_path = package_path / "PySide6"
     set_pyside_package_path(packaged_qt_tools_path)
     _pyside_tools = available_pyside_tools(packaged_qt_tools_path, package_for_wheels=True)
 
     # replacing pyside6-android_deploy by pyside6-android-deploy for consistency
-    # Also, the tool should not exist in any other platform than Linux
+    # Also, the tool should not exist in any other platform than Linux and macOS
     _console_scripts = []
-    if ("android_deploy" in _pyside_tools) and sys.platform.startswith("linux"):
+    if ("android_deploy" in _pyside_tools) and sys.platform in ("linux", "darwin"):
         _console_scripts = ['pyside6-android-deploy = "PySide6.scripts.pyside_tool:android_deploy"']
     _pyside_tools.remove("android_deploy")
 
@@ -276,7 +280,7 @@ def wheel_pyside6_essentials(package_path: Path) -> Tuple[SetupData, List[Module
     return setup, data
 
 
-def wheel_pyside6_addons(package_path: Path) -> Tuple[SetupData, List[ModuleData]]:
+def wheel_pyside6_addons(package_path: Path) -> tuple[SetupData, list[ModuleData]]:
     setup = SetupData(
         name="PySide6_Addons",
         version=get_version_from_package("PySide6", package_path),  # we use 'PySide6' here
@@ -290,7 +294,7 @@ def wheel_pyside6_addons(package_path: Path) -> Tuple[SetupData, List[ModuleData
     return setup, data
 
 
-def wheel_pyside6(package_path: Path) -> Tuple[SetupData, Optional[List[ModuleData]]]:
+def wheel_pyside6(package_path: Path) -> tuple[SetupData, list[ModuleData] | None]:
     setup = SetupData(
         name="PySide6",
         version=get_version_from_package("PySide6", package_path),
@@ -302,7 +306,7 @@ def wheel_pyside6(package_path: Path) -> Tuple[SetupData, Optional[List[ModuleDa
     return setup, None
 
 
-def wheel_pyside6_examples(package_path: Path) -> Tuple[SetupData, Optional[List[ModuleData]]]:
+def wheel_pyside6_examples(package_path: Path) -> tuple[SetupData, list[ModuleData] | None]:
     setup = SetupData(
         name="PySide6_Examples",
         version=get_version_from_package("PySide6", package_path),
@@ -397,6 +401,8 @@ if __name__ == "__main__":
         help="The directory where PySide was build (must contain a "
              "'package_for_wheels' folder"
     )
+    parser.add_argument("--no-examples", "-e", action="store_true",
+                        help="Do not build examples wheel")
     options = parser.parse_args()
 
     # Sanity check between the available modules,
@@ -439,8 +445,9 @@ if __name__ == "__main__":
         "PySide6_Essentials": wheel_pyside6_essentials,
         "PySide6_Addons": wheel_pyside6_addons,
         "PySide6": wheel_pyside6,
-        "PySide6_Examples": wheel_pyside6_examples,
     }
+    if not options.no_examples:
+        wheels["PySide6_Examples"] = wheel_pyside6_examples
 
     for name, wheel_info in wheels.items():
 
@@ -481,7 +488,7 @@ if __name__ == "__main__":
         print("-- Copy configuration files to create the wheel")
         if name == "PySide6_Examples":
             copy_examples_for_wheel(package_path)
-        _files: List[Path] = base_files + [Path(setup.readme)]
+        _files: list[Path] = base_files + [Path(setup.readme)]
         for fname in _files:
             copy(fname, package_path)
 
diff --git a/doc/changelogs/changes-6.7.3 b/doc/changelogs/changes-6.7.3
new file mode 100644 (file)
index 0000000..93ea360
--- /dev/null
@@ -0,0 +1,75 @@
+Qt for Python 6.7.3 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - A --flatpak option has been added to setup.py, enabling a flatpak build
+   of Qt for Python.
+ - [PYSIDE-769]  QtAsyncio: The application argument has been removed
+                 from the loop policy.
+ - [PYSIDE-1612] Deployment: Nuitka has been updated to 2.3.7. Scanning for
+                 QML dependencies has been fixed to skip some directories.
+ - [PYSIDE-1612] Android Cross Compilation: INSTSONAME has been added.
+ - [PYSIDE-1877] Properties of type QAbstractItemModel can now be used in QML.
+ - [PYSIDE-2192] PySide Qt Gui applications can now be used in interactive
+                 mode, for example notebooks.
+ - [PYSIDE-2517] Type hints: The signatures of QObject.findChild()/
+                 findChildren() have been improved to reflect the type passed
+                 in.
+ - [PYSIDE-2622] Deployment: Nuitka --standalone mode is now supported.
+ - [PYSIDE-2656] QtMultimedia on macOS has been fixed.
+ - [PYSIDE-2702] An option to force processing system headers has been added
+                 for cases where Qt is installed into the system.
+ - [PYSIDE-2752] Type hints: A syntax error caused by empty Enums has been
+                 fixed.
+ - [PYSIDE-2766] Android Deployment: pyside6-android-deploy now works macOS,
+                 too.
+ - [PYSIDE-2785] Deployment: 'dist-packages' is now skipped similar
+                 to 'site-packages' when scanning for QML dependencies.
+ - [PYSIDE-2788] Type hints: The signature of QFormLayout.getLayoutPosition()
+                 has been fixed.
+ - [PYSIDE-2789] numpy 2.0 is now supported.
+ - [PYSIDE-2790] QtAsyncio: cancel count and uncancel() have been added.
+ - [PYSIDE-2796] A potential crash in currentOpcode_Is_CallMethNoArgs()
+                 has been fixed.
+ - [PYSIDE-2799] QtAsyncio: A hang when an exception occurs inside a
+                 TaskGroup body has been fixed.
+ - [PYSIDE-2803] Desktop Deployment: Overflows of command lines on Windows
+                 have been fixed.
+ - [PYSIDE-2806] Desktop Deployment: The application name has been fixed.
+ - [PYSIDE-2814] Deployment: Arguments with spaces can now be used for
+                 "extra_args" due to using shlex for splitting the command
+                 line arguments.
+ - [PYSIDE-2819] The correct libclang is now used for arm64.
+ - [PYSIDE-2825] QtWebView has been added.
+ - [PYSIDE-2828] Documentation: The .qrc tutorial has been updated.
+ - [PYSIDE-2833] The QML tutorials have been updated.
+ - [PYSIDE-2834] QDir.entry(Info)List(QDir.Filter, QDir.SortFlags)
+                 has been fixed to work with Python 3.11 and later.
+ - [PYSIDE-2836] PySide6/__init__.py now has a static list of modules,
+                 enabling code checkers to work.
+ - [PYSIDE-2870] A crash when using QStateMachine.postEvent() has been
+                 fixed.
+
+****************************************************************************
+*                                  Shiboken6                               *
+****************************************************************************
+
+- [PYSIDE-2834] Enumerations have been excluded from argument type checks
+                for sequences. This addresses a problem showing in Python
+                3.11 causing the wrong function overloads to be used.
+- [PYSIDE-2780] A potential refcounting bug in Lazy loading has been fixed.
diff --git a/doc/changelogs/changes-6.8.0 b/doc/changelogs/changes-6.8.0
new file mode 100644 (file)
index 0000000..af5ee0f
--- /dev/null
@@ -0,0 +1,61 @@
+Qt for Python 6.8.0 is a minor release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - [PYSIDE-769]  QtAsyncio: The application argument has been removed
+                 from the loop policy.
+ - [PYSIDE-2221] The code for signal connections for Python callables not
+                 targeting a QMetaMethod has be reimplemented to simplify
+                 code and prepare for removal of the GIL.
+ - [PYSIDE-2404] Lazy loading has been extended to smart pointers
+                 and nested types.
+ - [PYSIDE-2620] pyside6-svgtoqml has been added.
+ - [PYSIDE-2701] The size of the generated code has been reduced.
+ - [PYSIDE-2701] Python formats with "#" in it are now supported.
+ - [PYSIDE-2711] An undesired invalidation of QObjects/QWidgets after
+                 calling parent(Widget)() has been fixed.
+ - [PYSIDE-2766] Android wheels are now provided.
+ - [PYSIDE-2786] Modern typing syntax is now used in examples and QtAsyncio
+                 code, enabled by a future import for Python 3.9, too.
+ - [PYSIDE-2805] An overload taking a slot connected to QMessageBox.accepted
+                 has been added to QMessageBox.open().
+ - [PYSIDE-2810] The number of QByteArray conversions in libpyside's
+                 signal handling has been reduced.
+ - [PYSIDE-2820] Opaque containers of type QList can now be used for initial
+                 properties in QML.
+ - [PYSIDE-2803] Deployment: An issue in QML import scanning causing
+                 overly long command lines has been fixed.
+ - [PYSIDE-2837] The documentation has been restructured and updated.
+ - [PYSIDE-2859] QModelRoleDataSpan for implementing
+                 QAbstractItemModel.multiData() has been added.
+ - [PYSIDE-2840] Enumeration properties for Qt Designer custom widgets
+                 have been fixed.
+ - [PYSIDE-2845] QSemaphore.tryAcquire() has been fixed to work with
+                 the snake case feature. The existing C++ function
+                 try_acquire() has been removed.
+ - [PYSIDE-2846] Type hints: Many issues in the generated stub files
+                 have been fixed to enable checking with mypy.
+
+****************************************************************************
+*                                  Shiboken6                               *
+****************************************************************************
+
+ - [PYSIDE-2792] Potential mismatches caused by nested classes
+                 when looking up converters by name have been fixed.
+ - [PYSIDE-1735] The generated code for enumerations has been refactored
+                 to consider underlying types.
diff --git a/doc/changelogs/changes-6.8.1 b/doc/changelogs/changes-6.8.1
new file mode 100644 (file)
index 0000000..fe910bc
--- /dev/null
@@ -0,0 +1,53 @@
+Qt for Python 6.8.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - [PYSIDE-1106] Documentation: Inner classes are now shown nested
+                 into their outer classes for referencing to work correctly.
+ - [PYSIDE-2689] Documentation: A workaround has been made to fix the search
+                 context of class pages.
+ - [PYSIDE-1612] Deployment: The bundling of QML files has been improved
+                 by using nuitka's 'include-data-dir' command line option.
+ - [PYSIDE-2206] The OSM-buildings demo (maps part) and
+                 the zoomlinechart example have been added.
+ - [PYSIDE-2211] Type hints: Errors resulting from inconsistencies
+                 between binary operators and their corresponding
+                 in-place operators have been fixed.
+ - [PYSIDE-2751] Support for Python 3.13 has been added.
+ - [PYSIDE-2825] Missing libraries required for QtWebView have been added.
+ - [PYSIDE-2846] Type hints: mypy support has been greatly improved
+                 and the .pyi files are now tested.
+ - [PYSIDE-2884] Type hints: typing.Callable has been fixed to include the
+                 parameters.
+ - [PYSIDE-2886] Type hints: Multiple signatures and Union expressions
+                 have been fixed.
+ - [PYSIDE-2891] An issue affecting 32bit builds has been fixed.
+ - [PYSIDE-2893] The installation of the module doc snippet files has
+                 been fixed.
+ - [PYSIDE-2895] PySide's __init__.py no longer contains
+                 build-directory-dependent code and PySide6.__all__ has been
+                 fixed to again work.
+ - [PYSIDE-2901] Documentation: Tutorials and reference pages
+                 related to Signal and Slots have been improved.
+ - [PYSIDE-2903] Type hints: Duplicated occurrences of '#' in the pyi type
+                 specifications causing errors have been fixed.
+ - [PYSIDE-2910] Deployment: Bundling of the FluentWinUI3
+                 style for Qt Quick Controls has been fixed.
+ - [PYSIDE-2913] QWebEngineDesktopMediaRequest has been added.
+ - [PYSIDE-2915] Type hints: Rich compare functions are now supported.
+ - [PYSIDE-2917] numpy version 2 is now used Python 3.10+.
diff --git a/doc/changelogs/changes-6.8.1.1 b/doc/changelogs/changes-6.8.1.1
new file mode 100644 (file)
index 0000000..272bccf
--- /dev/null
@@ -0,0 +1,20 @@
+Qt for Python 6.8.1.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - [PYSIDE-2964] Added WebView to macOS wheels.
diff --git a/doc/changelogs/changes-6.8.2 b/doc/changelogs/changes-6.8.2
new file mode 100644 (file)
index 0000000..6a8eb62
--- /dev/null
@@ -0,0 +1,54 @@
+Qt for Python 6.8.2 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - [PYSIDE-487]  Some missing bindings were added.
+ - [PYSIDE-841]  The simple bar graph (QtGraphs) and zoomlinechart (QtCharts)
+                 examples have been added.
+ - [PYSIDE-1106] Documentation: A number of missing documentation pages has
+                 been added. The module pages were updated and improved, now
+                 showing the QML types more prominently. Parameter names and
+                 documentation of global and/or added functions have been
+                 added.
+ - [PYSIDE-1612] Deployment: Development folders are no longer included in the
+                 deployed executable. A bug parsing "from . import x" has been
+                 fixed. .webp files are now supported. Nuitka has been upgraded
+                 to 2.5.1. Non-existent plugins are now disregarded.
+ - [PYSIDE-1612] Android Deployment: The Android NDK is now automatically
+                 downloaded.
+ - [PYSIDE-1735] Examples and tests have been changed to use fully qualified
+                 enumerations.
+ - [PYSIDE-2624] Type hints: Positional parameters are now used according to
+                 PEP 570.
+ - [PYSIDE-2846] Type hints: Properties in constructors are now supported
+                 (including snake_case). Empty enums have been fixed for
+                 mypy 1.14.
+ - [PYSIDE-2846] A crash related to inner classes in Qt3DRender has been fixed.
+ - [PYSIDE-2936] A crash using QDBusUnixFileDescriptor in
+                 QDBusAbstractInterface::call() has been fixed.
+ - [PYSIDE-2939] Type hints: The heuristic for ambiguous signature removal
+                 has been fixed.
+ - [PYSIDE-2941] Type hints: Some optional return annotations have been fixed.
+ - [PYSIDE-2946] A crash registering smart pointer types conversions of class
+                 hierarchies has been fixed.
+
+****************************************************************************
+*                                  Shiboken6                               *
+****************************************************************************
+
+ - [PYSIDE-2701] The generated code has been streamlined.
diff --git a/doc/changelogs/changes-6.8.2.1 b/doc/changelogs/changes-6.8.2.1
new file mode 100644 (file)
index 0000000..47d2ca3
--- /dev/null
@@ -0,0 +1,21 @@
+Qt for Python 6.8.2.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - [PYSIDE-3000] A bug in 6.8.2 causing the MSVC runtime libraries no longer
+                 being shipped has been fixed.
index eef0dca5dbedb50c9589c169f8bf236d921a0d71..23ceae8549850eb5f39ee788da905e019b856764 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qt3d/simple-cpp example from Qt v5.x"""
 
index c5d7fc2fe9e7c14de54e274acb7589b310ae7d55..54378daae761e07cf2ad64ed669535335556e285 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import (Qt, QObject, Signal, Slot)
 from PySide6.QtGui import (QColor, QFont, QPalette)
index 89d3d1ce606862efe3bcb9d723cff99d182b530e..9530077e89b5d1ef0ad67823c662d81f3dbedc19 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import (Qt, QEvent, QObject, QTimer, Signal, Slot)
 from PySide6.QtGui import (QColor, QFont, QPalette)
index a6c4708b3d90af9e64d265ccddc34b0ac1ab357c..7ffd5030f92c19b6ba7a16ab0eaef0cd31a82138 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt
 from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
index 5ae19fa36b53e4be04d2dade271ba27997f615f2..5d331c109fb460868e58384c2089b433f5042cef 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import (Qt, QEvent, QObject, Signal, Slot)
 from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
index bcd4c27148d261b683896f8f6ecc49ef7bbbeaf7..a591b16899c5e3b2ff99e0bcfff4f5901e816369 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 Active Qt Viewer example"""
 
index c75f5b8a1df078f044feca0cbf314d0ce018e006..b36673a4e4bc1f3f3d4639278bb4fb861d208b30 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QPoint, Qt, Slot
 from PySide6.QtGui import QColor
@@ -33,7 +34,7 @@ class DeviceDiscoveryDialog(QDialog):
 
         self.host_mode_state_changed(self._local_device.hostMode())
         # add context menu for devices to be able to pair device
-        self._ui.list.setContextMenuPolicy(Qt.CustomContextMenu)
+        self._ui.list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
         self._ui.list.customContextMenuRequested.connect(self.display_pairing_menu)
         self._local_device.pairingFinished.connect(self.pairing_done)
 
@@ -45,11 +46,11 @@ class DeviceDiscoveryDialog(QDialog):
         if not items:
             item = QListWidgetItem(label)
             pairing_status = self._local_device.pairingStatus(info.address())
-            if (pairing_status == QBluetoothLocalDevice.Paired
-                    or pairing_status == QBluetoothLocalDevice.AuthorizedPaired):
-                item.setForeground(QColor(Qt.green))
+            if (pairing_status == QBluetoothLocalDevice.Pairing.Paired
+                    or pairing_status == QBluetoothLocalDevice.Pairing.AuthorizedPaired):
+                item.setForeground(QColor(Qt.GlobalColor.green))
             else:
-                item.setForeground(QColor(Qt.black))
+                item.setForeground(QColor(Qt.GlobalColor.black))
             self._ui.list.addItem(item)
 
     @Slot()
@@ -77,9 +78,9 @@ class DeviceDiscoveryDialog(QDialog):
     @Slot(bool)
     def on_discoverable_clicked(self, clicked):
         if clicked:
-            self._local_device.setHostMode(QBluetoothLocalDevice.HostDiscoverable)
+            self._local_device.setHostMode(QBluetoothLocalDevice.HostMode.HostDiscoverable)
         else:
-            self._local_device.setHostMode(QBluetoothLocalDevice.HostConnectable)
+            self._local_device.setHostMode(QBluetoothLocalDevice.HostMode.HostConnectable)
 
     @Slot(bool)
     def on_power_clicked(self, clicked):
@@ -90,10 +91,10 @@ class DeviceDiscoveryDialog(QDialog):
 
     @Slot("QBluetoothLocalDevice::HostMode")
     def host_mode_state_changed(self, mode):
-        self._ui.power.setChecked(mode != QBluetoothLocalDevice.HostPoweredOff)
-        self._ui.discoverable.setChecked(mode == QBluetoothLocalDevice.HostDiscoverable)
+        self._ui.power.setChecked(mode != QBluetoothLocalDevice.HostMode.HostPoweredOff)
+        self._ui.discoverable.setChecked(mode == QBluetoothLocalDevice.HostMode.HostDiscoverable)
 
-        on = mode != QBluetoothLocalDevice.HostPoweredOff
+        on = mode != QBluetoothLocalDevice.HostMode.HostPoweredOff
         self._ui.scan.setEnabled(on)
         self._ui.discoverable.setEnabled(on)
 
index a54a862a20612e5814e72e7f7819ff3740e50c92..cab83de28f7a31c55ddbc88deeefe9418f10cc86 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the bluetooth/btscanner example from Qt v6.x"""
 
index 31df8a9ea5a60209d705fea66f81b53f562cb210..928c8cde2cc677ee929a9d374ebce60551ee4087 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtWidgets import QDialog
index 4ca12ee05c4dc624c7606a268ff480146ebe7f1a..5a4bd637dc3a57d22f4541edb503b90b3d13bf68 100644 (file)
@@ -27,7 +27,7 @@
    <item>
     <widget class="QDialogButtonBox" name="buttonBox">
      <property name="standardButtons">
-      <set>QDialogButtonBox::Close</set>
+      <set>QDialogButtonBox::StandardButton::Close</set>
      </property>
     </widget>
    </item>
index b443b2bc24a91a828c7ee5fefcbd2f4c453cc707..bf56002dc7b7f6ade833da5e77c14ea875c85be1 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'device.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
index ccc36677a4b97b59cdb17aed20cd0d74e7e0e25f..151dacebf361cb19626930d0ad4fdda093c760e7 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'service.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -38,7 +38,7 @@ class Ui_ServiceDiscovery(object):
 
         self.buttonBox = QDialogButtonBox(ServiceDiscovery)
         self.buttonBox.setObjectName(u"buttonBox")
-        self.buttonBox.setStandardButtons(QDialogButtonBox.Close)
+        self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Close)
 
         self.verticalLayout.addWidget(self.buttonBox)
 
index cc5c9dbd08191827054339f71ad4b32c96cc736d..6278b04181e90526f4299204020635dee864301f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal, Slot
 
index 7bf60bbc50a6aba6d1d402f4a678366c3fbcd140..17b1500d988dc452374f47b64c7622da9a7b924b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index e581d12ec02209da4da25dce2143d4b3e2ecbe42..5c00e7c94d74f760401c6e8429cc09ab69dabfd0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 import sys
 
 from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent,
index df34052b8ffa9555be3b9b848f697e11d11be19a..8599c029659e156be480280f90b6aeaf94fb40c7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import struct
 
@@ -57,7 +58,7 @@ class DeviceHandler(BluetoothBaseClass):
         self.m_stop = QDateTime()
 
         self.m_measurements = []
-        self.m_addressType = QLowEnergyController.PublicAddress
+        self.m_addressType = QLowEnergyController.RemoteAddressType.PublicAddress
 
         self.m_demoTimer = QTimer()
 
@@ -70,16 +71,16 @@ class DeviceHandler(BluetoothBaseClass):
 
     @Property(int)
     def addressType(self):
-        if self.m_addressType == QLowEnergyController.RandomAddress:
+        if self.m_addressType == QLowEnergyController.RemoteAddressType.RandomAddress:
             return DeviceHandler.AddressType.RANDOM_ADDRESS
         return DeviceHandler.AddressType.PUBLIC_ADDRESS
 
     @addressType.setter
     def addressType(self, type):
         if type == DeviceHandler.AddressType.PUBLIC_ADDRESS:
-            self.m_addressType = QLowEnergyController.PublicAddress
+            self.m_addressType = QLowEnergyController.RemoteAddressType.PublicAddress
         elif type == DeviceHandler.AddressType.RANDOM_ADDRESS:
-            self.m_addressType = QLowEnergyController.RandomAddress
+            self.m_addressType = QLowEnergyController.RemoteAddressType.RandomAddress
 
     @Slot(QLowEnergyController.Error)
     def controllerErrorOccurred(self, device):
index 5fd5c32705e269e1ba6f6341e751849564e45ece..136bbbacbf165152a753d6d5c9dbe79936cd629c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index de5c37ac3bea1cce0f7505b1708b4e3f8c61d8e7..384eb93a7d5e234af62443578aef2a220e22ff51 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 import os
 import sys
 
index 3cb4f0672275b5a835bb3f04664d13fc2da78b8f..00df7de6a8c63ae83cc29fb2d06650431cd58aa6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the bluetooth/heartrate-game example from Qt v6.x"""
 
@@ -48,6 +49,6 @@ if __name__ == '__main__':
     if not engine.rootObjects():
         sys.exit(-1)
 
-    ex = QCoreApplication.exec()
+    exit_code = QCoreApplication.exec()
     del engine
-    sys.exit(ex)
+    sys.exit(exit_code)
index abbf4eb7f80b2a0e9f9de230b13740df001ee0bc..2bb8e3af0cc267d1393b0d052a298f699d69e263 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the bluetooth/heartrate-server example from Qt v6.x"""
 
@@ -28,7 +29,7 @@ if __name__ == '__main__':
 
 #! [Advertising Data]
     advertising_data = QLowEnergyAdvertisingData()
-    advertising_data.setDiscoverability(QLowEnergyAdvertisingData.DiscoverabilityGeneral)
+    advertising_data.setDiscoverability(QLowEnergyAdvertisingData.Discoverability.DiscoverabilityGeneral)  # noqa: E501
     advertising_data.setIncludePowerLevel(True)
     advertising_data.setLocalName("HeartRateServer")
     advertising_data.setServices([QBluetoothUuid.ServiceClassUuid.HeartRate])
@@ -38,13 +39,13 @@ if __name__ == '__main__':
     char_data = QLowEnergyCharacteristicData()
     char_data.setUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
     char_data.setValue(QByteArray(2, 0))
-    char_data.setProperties(QLowEnergyCharacteristic.Notify)
+    char_data.setProperties(QLowEnergyCharacteristic.PropertyType.Notify)
     client_config = QLowEnergyDescriptorData(
         QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration, QByteArray(2, 0))
     char_data.addDescriptor(client_config)
 
     service_data = QLowEnergyServiceData()
-    service_data.setType(QLowEnergyServiceData.ServiceTypePrimary)
+    service_data.setType(QLowEnergyServiceData.ServiceType.ServiceTypePrimary)
     service_data.setUuid(QBluetoothUuid.ServiceClassUuid.HeartRate)
     service_data.addCharacteristic(char_data)
 #! [Service Data]
index 42bde8753d01dfa310ba96892355b905bd2b877d..18f4c4202a52cc8df828db9fa213a9680dcb27ff 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtBluetooth import QLowEnergyCharacteristic, QBluetoothUuid
index 09108cf69cd5bf1015296560a42bfd6f551de61c..afa30ff80cdd9f641271bd414d9dbce87d206719 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 import warnings
 from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent, QLowEnergyController,
                                  QBluetoothDeviceInfo, QBluetoothUuid, QLowEnergyService)
index 35a568821ea16e58c964c364daf7ef46068fe8b6..0952f8d74bcccd847b83461d870281a3740a8ca5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index ec12f99e73d9e385634753237f40f3a6a250da62..35c82ed37dc065f5f80094a63a596af5adad9372 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the bluetooth/lowenergyscanner example from Qt v6.x"""
 
@@ -22,6 +23,6 @@ if __name__ == '__main__':
     if not engine.rootObjects():
         sys.exit(-1)
 
-    ex = QCoreApplication.exec()
+    exit_code = QCoreApplication.exec()
     del engine
-    sys.exit(ex)
+    sys.exit(exit_code)
index cddffe6636196e9ccd3d6a3d339405f74039be4f..30df4e0bef92a1604a88a7474a91f62b93ad3ad8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtBluetooth import QLowEnergyService
index 164264ff71be836175fa8bbc882f2c0dcb223ab2..4fdd358784ccd2c3e015d78d3683f0052842916c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the areachart example from Qt v6.x"""
 
@@ -49,11 +50,11 @@ class TestChart(QMainWindow):
         self.chart.addSeries(self.series)
         self.chart.setTitle("Simple areachart example")
         self.chart.createDefaultAxes()
-        self.chart.axes(Qt.Horizontal)[0].setRange(0, 20)
+        self.chart.axes(Qt.Orientation.Horizontal)[0].setRange(0, 20)
         self.chart.axes(Qt.Vertical)[0].setRange(0, 10)
 
         self._chart_view = QChartView(self.chart)
-        self._chart_view.setRenderHint(QPainter.Antialiasing)
+        self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(self._chart_view)
 
index 8ba0b20c5c24c9cf69c09a32ea1bebde4af1b876..1d603f880dd2d00f53f302dcb21920bbc573c1de 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the charts/audio example from Qt v5.x"""
 
index 66126ad8a8a1f57692bdf80b09a306c81be6d0ee..793a013de278b8a501b0ef41e95476b593f3cfce 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the linechart example from Qt v6.x"""
 
@@ -55,7 +56,7 @@ class TestChart(QMainWindow):
         self.chart.legend().setAlignment(Qt.AlignBottom)
 
         self._chart_view = QChartView(self.chart)
-        self._chart_view.setRenderHint(QPainter.Antialiasing)
+        self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(self._chart_view)
 
index 622938d1664ebfc3678d5114a06ecca293527811..d8ba8e6e8c1e80a76546c292ecc90d60964ed91b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Callout example from Qt v5.x"""
 
@@ -148,7 +149,7 @@ class View(QGraphicsView):
         self._chart.createDefaultAxes()
         self._chart.setAcceptHoverEvents(True)
 
-        self.setRenderHint(QPainter.Antialiasing)
+        self.setRenderHint(QPainter.RenderHint.Antialiasing)
         self.scene().addItem(self._chart)
 
         self._coordX = QGraphicsSimpleTextItem(self._chart)
index 5787710ca236de15e67672e70fc67d138c60368f..b7a57029a96b509b5d969e99c5215f2500ff6dbd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Chart Themes example from Qt v5.x"""
 
@@ -144,7 +145,7 @@ class ThemeWidget(QWidget):
             lower_series = upper_series
 
         chart.createDefaultAxes()
-        axis_x = chart.axes(Qt.Horizontal)[0]
+        axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
         axis_x.setRange(0, self.value_count - 1)
         axis_y = chart.axes(Qt.Vertical)[0]
         axis_y.setRange(0, self.value_max)
@@ -187,7 +188,7 @@ class ThemeWidget(QWidget):
             chart.addSeries(series)
 
         chart.createDefaultAxes()
-        axis_x = chart.axes(Qt.Horizontal)[0]
+        axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
         axis_x.setRange(0, self.value_max)
         axis_y = chart.axes(Qt.Vertical)[0]
         axis_y.setRange(0, self.value_count)
@@ -226,7 +227,7 @@ class ThemeWidget(QWidget):
             chart.addSeries(series)
 
         chart.createDefaultAxes()
-        axis_x = chart.axes(Qt.Horizontal)[0]
+        axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
         axis_x.setRange(0, self.value_max)
         axis_y = chart.axes(Qt.Vertical)[0]
         axis_y.setRange(0, self.value_count)
@@ -247,7 +248,7 @@ class ThemeWidget(QWidget):
             chart.addSeries(series)
 
         chart.createDefaultAxes()
-        axis_x = chart.axes(Qt.Horizontal)[0]
+        axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
         axis_x.setRange(0, self.value_max)
         axis_y = chart.axes(Qt.Vertical)[0]
         axis_y.setRange(0, self.value_count)
@@ -293,7 +294,7 @@ class ThemeWidget(QWidget):
         # Update antialiasing
         checked = self.ui.antialiasCheckBox.isChecked()
         for chart in self.charts:
-            chart.setRenderHint(QPainter.Antialiasing, checked)
+            chart.setRenderHint(QPainter.RenderHint.Antialiasing, checked)
 
         # Update animation options
         idx = self.ui.animatedComboBox.currentIndex()
index ad1e02f06d9f7f4c6b0283b2135682c23c75cab5..2e42a9d5539b14515de4caef5d30d55b110fcc5c 100644 (file)
@@ -56,7 +56,7 @@
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
-        <enum>Qt::Horizontal</enum>
+        <enum>Qt::Orientation::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
    <signal>currentIndexChanged(int)</signal>
    <receiver>ThemeWidgetForm</receiver>
    <slot>update_ui()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
   </connection>
   <connection>
    <sender>antialiasCheckBox</sender>
    <signal>toggled(bool)</signal>
    <receiver>ThemeWidgetForm</receiver>
    <slot>update_ui()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
   </connection>
   <connection>
    <sender>legendComboBox</sender>
    <signal>currentIndexChanged(int)</signal>
    <receiver>ThemeWidgetForm</receiver>
    <slot>update_ui()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
   </connection>
   <connection>
    <sender>animatedComboBox</sender>
    <signal>currentIndexChanged(int)</signal>
    <receiver>ThemeWidgetForm</receiver>
    <slot>update_ui()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>20</x>
+     <y>20</y>
+    </hint>
+   </hints>
   </connection>
  </connections>
  <slots>
index 711615b3e70df1f24b0283a9dab500216d1418a0..c46e49059b48aa03fba6b3a6e6d34f98824cccda 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'themewidget.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
index 9111d4aacc843cc29bf0a46af4685c0f41dd4e70..d6792357a0d718d16c3264df77d51eb42578e5cd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Donut Chart Breakdown example from Qt v5.x"""
 
@@ -138,7 +139,7 @@ if __name__ == "__main__":
 
     window = QMainWindow()
     chart_view = QChartView(donut_breakdown)
-    chart_view.setRenderHint(QPainter.Antialiasing)
+    chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
     window.setCentralWidget(chart_view)
     available_geometry = window.screen().availableGeometry()
     size = available_geometry.height() * 0.75
index 59ddef13f92bdc5f2b1dc74ddfca1ef8cc1c9a67..0858007f1d0abb39b9fa9bb581ad3ff199d5d08a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import random
 
index 8405824dc31fc3abe2c3d4633fd39dc2150bb41c..f74614e55916f86ec0d607fff8f5423c29861567 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Dynamic Spline example from Qt v5.x"""
 import sys
@@ -17,9 +18,9 @@ if __name__ == "__main__":
     chart = Chart()
     chart.setTitle("Dynamic spline chart")
     chart.legend().hide()
-    chart.setAnimationOptions(QChart.AllAnimations)
+    chart.setAnimationOptions(QChart.AnimationOption.AllAnimations)
     chart_view = QChartView(chart)
-    chart_view.setRenderHint(QPainter.Antialiasing)
+    chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
     window.setCentralWidget(chart_view)
     window.resize(400, 300)
     window.show()
index 5417a940f6e254af27972a7b0691bf681467290e..cdc420520d8d91df6615b4ae30eecbf3f26e0e63 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Legend example from Qt v5.x"""
 
@@ -99,7 +100,7 @@ class MainWidget(QWidget):
         self.chart.legend().setVisible(True)
         self.chart.legend().setAlignment(Qt.AlignBottom)
 
-        self.chart_view.setRenderHint(QPainter.Antialiasing)
+        self.chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
     def show_legend_spinbox(self):
         self.legend_settings.setVisible(True)
index 62721c763409d30d1fc48917e351d0420010bb54..fe559a64eb094bdf3d5cd36c0f24a513ac896797 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the line/bar example from Qt v5.x"""
 
@@ -66,7 +67,7 @@ class TestChart(QMainWindow):
         self.chart.legend().setAlignment(Qt.AlignBottom)
 
         self._chart_view = QChartView(self.chart)
-        self._chart_view.setRenderHint(QPainter.Antialiasing)
+        self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(self._chart_view)
 
index cee4c16dbc21c38398274cdbf7bd9e7e8295834b..3a779e83ef9fd07982aaea30ec2df31d409584ba 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the linechart example from Qt v5.x"""
 
@@ -33,7 +34,7 @@ class TestChart(QMainWindow):
         self.chart.setTitle("Simple line chart example")
 
         self._chart_view = QChartView(self.chart)
-        self._chart_view.setRenderHint(QPainter.Antialiasing)
+        self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(self._chart_view)
 
index e727c1200c495ff1f19a58616cb9d0940352ae7c..5e86eaf22a9e4ae9de78384162d7ed70585f6984 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Logarithmic Axis Example from Qt v5.x"""
 
@@ -42,7 +43,7 @@ class TestChart(QMainWindow):
         self.series.attachAxis(self._axis_y)
 
         self._chart_view = QChartView(self.chart)
-        self._chart_view.setRenderHint(QPainter.Antialiasing)
+        self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(self._chart_view)
 
index 3ba42368ef05aec6aa34e1347fdff28658a8a868..543b5f562e3211334010e5b48744fb04f2e1527d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 Charts example: Simple memory usage viewer"""
 
index 0e36f777032786c1571e944a491b1e33c69bf68f..5fd88be32179b8c6828d9e03e991529db1d8cd32 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Model Data example from Qt v5.x"""
 
@@ -37,10 +38,10 @@ class CustomTableModel(QAbstractTableModel):
         return self.column_count
 
     def headerData(self, section, orientation, role):
-        if role != Qt.DisplayRole:
+        if role != Qt.ItemDataRole.DisplayRole:
             return None
 
-        if orientation == Qt.Horizontal:
+        if orientation == Qt.Orientation.Horizontal:
             if section % 2 == 0:
                 return "x"
             else:
@@ -48,12 +49,12 @@ class CustomTableModel(QAbstractTableModel):
         else:
             return str(section + 1)
 
-    def data(self, index, role=Qt.DisplayRole):
-        if role == Qt.DisplayRole:
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+        if role == Qt.ItemDataRole.DisplayRole:
             return self.input_data[index.row()][index.column()]
-        elif role == Qt.EditRole:
+        elif role == Qt.ItemDataRole.EditRole:
             return self.input_data[index.row()][index.column()]
-        elif role == Qt.BackgroundRole:
+        elif role == Qt.ItemDataRole.BackgroundRole:
             for color, rect in self.mapping.items():
                 if rect.contains(index.column(), index.row()):
                     return QColor(color)
@@ -61,8 +62,8 @@ class CustomTableModel(QAbstractTableModel):
             return QColor(Qt.white)
         return None
 
-    def setData(self, index, value, role=Qt.EditRole):
-        if index.isValid() and role == Qt.EditRole:
+    def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
+        if index.isValid() and role == Qt.ItemDataRole.EditRole:
             self.input_data[index.row()][index.column()] = float(value)
             self.dataChanged.emit(index, index)
             return True
@@ -122,7 +123,7 @@ class TableWidget(QWidget):
 
         self.chart.createDefaultAxes()
         self.chart_view = QChartView(self.chart)
-        self.chart_view.setRenderHint(QPainter.Antialiasing)
+        self.chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
         self.chart_view.setMinimumSize(640, 480)
 
         # create main layout
index 49173c33a13001ae363366df626147dd3082dd68..bb566ffa6e32fc5af66134145eba0eaba9b1c879 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Nested Donuts example from Qt v5.x"""
 
@@ -21,7 +22,7 @@ class Widget(QWidget):
         self.setMinimumSize(800, 600)
         self.donuts = []
         self.chart_view = QChartView()
-        self.chart_view.setRenderHint(QPainter.Antialiasing)
+        self.chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
         self.chart = self.chart_view.chart()
         self.chart.legend().setVisible(False)
         self.chart.setTitle("Nested donuts demo")
index 9f70c03285864aa656289bf1a54dd2c116ed0048..82f64e99397b3f24d6ce3fde646385d82ad47e6f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Percent Bar Chart example from Qt v5.x"""
 
@@ -50,7 +51,7 @@ class MainWindow(QMainWindow):
         chart.legend().setAlignment(Qt.AlignBottom)
 
         chart_view = QChartView(chart)
-        chart_view.setRenderHint(QPainter.Antialiasing)
+        chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(chart_view)
 
index e71359a98c9dcf62b714e508ce191307c75d158c..317e17fecb2628ef904cbe86b1c1b596ca939ace 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Pie Chart Example from Qt v5.x"""
 
@@ -35,7 +36,7 @@ class TestChart(QMainWindow):
         self.chart.legend().hide()
 
         self._chart_view = QChartView(self.chart)
-        self._chart_view.setRenderHint(QPainter.Antialiasing)
+        self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(self._chart_view)
 
index 36b10aa1633de52400f02c3c159784043d41d5be..70ef84268b45db2b46e807b2510e07e099dd0c48 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Selected Point Configuration Example from Qt 6.5"""
 from PySide6.QtCore import QPointF, Slot
@@ -7,7 +8,6 @@ from PySide6.QtGui import QColor, QIcon, QPainter
 from PySide6.QtWidgets import QMainWindow, QLineEdit, QLabel, QComboBox
 from PySide6.QtWidgets import QCheckBox, QWidget, QGridLayout, QHBoxLayout
 from PySide6.QtCharts import QLineSeries, QXYSeries, QChart, QChartView
-from typing import Union
 
 
 PointConfig = QXYSeries.PointConfiguration
@@ -95,7 +95,7 @@ class ChartWindow(QMainWindow):
         self._select_point(4)
 
     @Slot(QPointF)
-    def _select_point(self, point: Union[QPointF, int]):
+    def _select_point(self, point: QPointF | int):
         try:
             index = (self._series.points().index(point.toPoint()) if
                      isinstance(point, QPointF) else point)
index ffe865d5e1b719b6ac237e1f42a8bbbc1b1cab92..479d7b4e1c7ea7dd2ce4adda391cb8c4ae663c1f 100644 (file)
@@ -141,4 +141,4 @@ and ``@index``. These are replaced with the x value, y value, and index of the p
 More information about that can be found in the documentation for
 `QtCharts.QXYSeries.pointLabelsFormat`_.
 
-.. _`QtCharts.QXYSeries.pointLabelsFormat`: https://doc.qt.io/qtforpython/PySide6/QtCharts/QXYSeries.html#PySide6.QtCharts.PySide6.QtCharts.QXYSeries.pointLabelsFormat
+.. _`QtCharts.QXYSeries.pointLabelsFormat`: https://doc.qt.io/qt-6/qxyseries.html#pointLabelsFormat-prop
index d8c90d2dfcc418e12d08231bba101f65d496d39c..b7bf43064398a6ce3a39e45c732de3c484d65dfd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Light Markers Points Selection example from Qt v6.2"""
 import sys
index df7b61687f3ae6b7bee8d0ebc46a73344958e381..a8064c1b3dfbff6353342479bafe405fd76748b5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Light Markers Points Selection example from Qt v6.2"""
 import sys
@@ -48,7 +49,7 @@ if __name__ == "__main__":
     chart.legend().setVisible(False)
 
     chart_view = QChartView(chart)
-    chart_view.setRenderHint(QPainter.Antialiasing)
+    chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
     control_widget = QWidget(window)
     control_layout = QGridLayout(control_widget)
@@ -87,7 +88,7 @@ if __name__ == "__main__":
 
     @Slot(int)
     def display_unselected_points(checkbox_state):
-        if checkbox_state:
+        if checkbox_state == Qt.CheckState.Checked:
             series.setLightMarker(
                 Utilities.get_point_representation(
                     Utilities.point_type(char_point_combobox.currentIndex()), marker_size))
@@ -96,7 +97,7 @@ if __name__ == "__main__":
 
     show_unselected_points_label = QLabel("Display unselected points: ")
     show_unselected_points_checkbox.setChecked(True)
-    show_unselected_points_checkbox.stateChanged.connect(display_unselected_points)
+    show_unselected_points_checkbox.checkStateChanged.connect(display_unselected_points)
 
     control_label = QLabel("Marker and Selection Controls")
     control_label.setAlignment(Qt.AlignHCenter)
index b27a2542b7e9c077e723d9d11029fdebef04ebae..124cdad4d73535b07c37823f032014e4942e3bbc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtGui import QImage, QPainter, QColor
 from PySide6.QtCore import Qt
@@ -11,7 +12,7 @@ def rectangle(point_type, image_size):
     image = QImage(image_size, image_size, QImage.Format_RGB32)
     painter = QPainter()
     painter.begin(image)
-    painter.setRenderHint(QPainter.Antialiasing)
+    painter.setRenderHint(QPainter.RenderHint.Antialiasing)
     painter.fillRect(0, 0, image_size, image_size, point_type[2])
     painter.end()
     return image
@@ -26,7 +27,7 @@ def circle(point_type, image_size):
     image.fill(QColor(0, 0, 0, 0))
     painter = QPainter()
     painter.begin(image)
-    painter.setRenderHint(QPainter.Antialiasing)
+    painter.setRenderHint(QPainter.RenderHint.Antialiasing)
     painter.setBrush(point_type[2])
     pen = painter.pen()
     pen.setWidth(0)
index 6391fc3058428e6f4f22f64a383dc33b088375dc..594ac7a6ad8985967e98f37a9acce86afcc6b0c3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the QML Polar Chart Example from Qt v5.x"""
 
index 358f3818e01574f17f39b11795f55c0ff9280957..ad1cdbf72c8828bba82ca4238ffa75ff7617fa0a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Temperature Records example from Qt v5.x"""
 
@@ -47,7 +48,7 @@ class MainWindow(QMainWindow):
         chart.legend().setAlignment(Qt.AlignBottom)
 
         chart_view = QChartView(chart)
-        chart_view.setRenderHint(QPainter.Antialiasing)
+        chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         self.setCentralWidget(chart_view)
 
diff --git a/examples/charts/zoomlinechart/chart.py b/examples/charts/zoomlinechart/chart.py
new file mode 100644 (file)
index 0000000..2b83e57
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from PySide6.QtWidgets import QGesture, QGestureEvent
+from PySide6.QtCore import Qt, QEvent
+from PySide6.QtCharts import QChart
+
+
+class Chart(QChart):
+    def __init__(self,
+                 ChartType=QChart.ChartType.ChartTypeCartesian,
+                 QGraphicsItem=None,
+                 WindowType=Qt.WindowFlags):
+        super().__init__()
+
+        self.grabGesture(Qt.PanGesture)
+        self.grabGesture(Qt.PinchGesture)
+
+    def sceneEvent(self, event: QEvent):
+
+        if event.type() == QEvent.Gesture:
+            return self.gestureEvent(event)
+
+        return super().sceneEvent(event)
+
+    def gestureEvent(self, event: QGestureEvent):
+
+        if gesture := event.gesture(Qt.PanGesture):
+            pan = gesture
+            self.scroll(-pan.delta().x(), pan.delta().y())
+
+        if gesture := event.gesture(Qt.PinchGesture):
+            pinch = gesture
+
+            if pinch.changeFlags() & QGesture.QPinchGesture.ScaleFactorChanged:
+                self.zoom(pinch.scaleFactor())
+
+        return True
diff --git a/examples/charts/zoomlinechart/chartview.py b/examples/charts/zoomlinechart/chartview.py
new file mode 100644 (file)
index 0000000..b4dcab9
--- /dev/null
@@ -0,0 +1,70 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from PySide6.QtCharts import QChart, QChartView
+from PySide6.QtGui import QKeyEvent, QMouseEvent
+from PySide6.QtCore import QEvent, Qt
+from PySide6.QtWidgets import QGraphicsView
+
+
+class ChartView(QChartView):
+    def __init__(self, chart, parent=None):
+        super().__init__(chart, parent)
+
+        self.setRubberBand(QChartView.RectangleRubberBand)
+        self._isTouching = False
+
+    def viewPortEvent(self, event: QEvent):
+
+        if event.type() == QMouseEvent.TouchBegin:
+            self._isTouching = True
+
+            self.chart().setAnimationOptions(QChart.NoAnimation)
+
+        return super().viewPortEvent(event)
+
+    def mousePressEvent(self, event: QMouseEvent):
+
+        if self._isTouching:
+            return
+
+        return super().mousePressEvent(event)
+
+    def mouseMoveEvent(self, event: QMouseEvent):
+
+        if self._isTouching:
+            return
+
+        return super().mouseMoveEvent(event)
+
+    def mouseReleaseEvent(self, event: QMouseEvent):
+
+        if self._isTouching:
+            self._isTouching = False
+
+        self.chart().setAnimationOptions(QChart.SeriesAnimations)
+
+        return super().mouseReleaseEvent(event)
+
+    def keyPressEvent(self, event: QKeyEvent):
+
+        key = event.key()
+        if key == Qt.Key_Plus:
+            self.chart().zoomIn()
+
+        elif key == Qt.Key_Minus:
+            self.chart().zoomOut()
+
+        elif key == Qt.Key_Left:
+            self.chart().scroll(-10, 0)
+
+        elif key == Qt.Key_Right:
+            self.chart().scroll(10, 0)
+
+        elif key == Qt.Key_Up:
+            self.chart().scroll(0, 10)
+
+        elif key == Qt.Key_Down:
+            self.chart().scroll(0, -10)
+
+        else:
+            QGraphicsView.keyPressEvent(event)
diff --git a/examples/charts/zoomlinechart/doc/zoomlinechart.png b/examples/charts/zoomlinechart/doc/zoomlinechart.png
new file mode 100644 (file)
index 0000000..528d557
Binary files /dev/null and b/examples/charts/zoomlinechart/doc/zoomlinechart.png differ
diff --git a/examples/charts/zoomlinechart/doc/zoomlinechart.rst b/examples/charts/zoomlinechart/doc/zoomlinechart.rst
new file mode 100644 (file)
index 0000000..f41c421
--- /dev/null
@@ -0,0 +1,8 @@
+Zoom Line Chart Example
+=======================
+
+The example shows how to create a zoomable simple sinusoidal line chart.
+
+.. image:: zoomlinechart.png
+   :width: 400
+   :alt: Zoom Line Chart Screenshot
diff --git a/examples/charts/zoomlinechart/main.py b/examples/charts/zoomlinechart/main.py
new file mode 100644 (file)
index 0000000..81af5e4
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import sys
+import math
+from PySide6.QtWidgets import QApplication, QMainWindow
+from PySide6.QtCore import Qt, QPointF, QRandomGenerator
+from PySide6.QtCharts import QChart, QLineSeries
+from PySide6.QtGui import QPainter
+
+import chartview
+import chart
+
+if __name__ == "__main__":
+
+    app = QApplication(sys.argv)
+
+    series = QLineSeries()
+
+    points = [
+        QPointF(float(i), math.sin(math.pi / 50 * i) * 100 + QRandomGenerator.global_().bounded(20))
+        for i in range(500)]
+
+    series.append(points)
+
+    line_chart = chart.Chart()
+    line_chart.addSeries(series)
+    line_chart.setTitle("Zoom in/out example")
+    line_chart.setAnimationOptions(QChart.SeriesAnimations)
+    line_chart.legend().hide()
+    line_chart.createDefaultAxes()
+
+    chart_view = chartview.ChartView(line_chart)
+    chart_view.setRenderHint(QPainter.Antialiasing, True)
+
+    window = QMainWindow()
+    window.setCentralWidget(chart_view)
+    window.resize(400, 300)
+    window.grabGesture(Qt.PanGesture)
+    window.grabGesture(Qt.PinchGesture)
+    window.show()
+
+    sys.exit(app.exec())
diff --git a/examples/charts/zoomlinechart/zoomlinechart.pyproject b/examples/charts/zoomlinechart/zoomlinechart.pyproject
new file mode 100644 (file)
index 0000000..ef5b77c
--- /dev/null
@@ -0,0 +1,7 @@
+{
+    "files": [
+        "chart.py",
+        "chartview.py",
+        "main.py"
+    ]
+}
index 1349000476e71ea4e1aa1c2f81e17d4924ac44ad..d957e47212d8a325c19fc3b783825715eeedba92 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from pathlib import Path
@@ -51,7 +52,7 @@ class Dialog(QDialog):
 
         # load into shared memory
         buffer = QBuffer()
-        buffer.open(QIODeviceBase.WriteOnly)
+        buffer.open(QIODeviceBase.OpenModeFlag.WriteOnly)
         out = QDataStream(buffer)
         out << image
         buffer.close()
@@ -78,7 +79,7 @@ class Dialog(QDialog):
         mv = memoryview(self._shared_memory.constData())
         buffer = QBuffer()
         buffer.setData(mv.tobytes())
-        buffer.open(QBuffer.ReadOnly)
+        buffer.open(QBuffer.OpenModeFlag.ReadOnly)
         _in = QDataStream(buffer)
         image = QImage()
         _in >> image
index e99d6fb3c42fbccea1bc86635cb0697dad78356c..fff08206c6f7d4d84aa3cf0c24c177075cc8273d 100644 (file)
@@ -1,7 +1,8 @@
-<ui version="4.0" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
  <class>Dialog</class>
- <widget class="QDialog" name="Dialog" >
-  <property name="geometry" >
+ <widget class="QDialog" name="Dialog">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <height>322</height>
    </rect>
   </property>
-  <property name="windowTitle" >
+  <property name="windowTitle">
    <string>Dialog</string>
   </property>
-  <layout class="QGridLayout" >
-   <item row="0" column="0" >
-    <widget class="QPushButton" name="loadFromFileButton" >
-     <property name="text" >
+  <layout class="QGridLayout">
+   <item row="0" column="0">
+    <widget class="QPushButton" name="loadFromFileButton">
+     <property name="text">
       <string>Load Image From File...</string>
      </property>
     </widget>
    </item>
-   <item row="1" column="0" >
-    <widget class="QLabel" name="label" >
-     <property name="text" >
+   <item row="1" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
       <string>Launch two of these dialogs.  In the first, press the top button and load an image from a file.  In the second, press the bottom button and display the loaded image from shared memory.</string>
      </property>
-     <property name="alignment" >
-      <set>Qt::AlignCenter</set>
+     <property name="alignment">
+      <set>Qt::AlignmentFlag::AlignCenter</set>
      </property>
-     <property name="wordWrap" >
+     <property name="wordWrap">
       <bool>true</bool>
      </property>
     </widget>
    </item>
-   <item row="2" column="0" >
-    <widget class="QPushButton" name="loadFromSharedMemoryButton" >
-     <property name="text" >
+   <item row="2" column="0">
+    <widget class="QPushButton" name="loadFromSharedMemoryButton">
+     <property name="text">
       <string>Display Image From Shared Memory</string>
      </property>
     </widget>
diff --git a/examples/corelib/ipc/sharedmemory/doc/sharedmemory.png b/examples/corelib/ipc/sharedmemory/doc/sharedmemory.png
new file mode 100644 (file)
index 0000000..53244d3
Binary files /dev/null and b/examples/corelib/ipc/sharedmemory/doc/sharedmemory.png differ
diff --git a/examples/corelib/ipc/sharedmemory/doc/sharedmemory.rst b/examples/corelib/ipc/sharedmemory/doc/sharedmemory.rst
new file mode 100644 (file)
index 0000000..01f8a32
--- /dev/null
@@ -0,0 +1,11 @@
+IPC: Shared Memory
+==================
+
+Demonstrates how to share image data between different processes using the
+Shared Memory IPC mechanism. It corresponds to the Qt example
+`Shared Memory Example <https://doc.qt.io/qt-6/qtcore-ipc-sharedmemory-example.html>`_.
+
+.. image:: sharedmemory.png
+    :align: center
+    :alt: sharedmemory screenshot
+    :width: 400
index e497c8de6e2590616faab73f780a3ecaf221229e..ef8733782fc74d95206e045d30e21766cc8c872d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the ipc/sharedmemory example from Qt v6.x"""
 
index 2cd544f401f80beb0b7ab1a806f4f3964c28d718..8676cecd9d584076474a9fad493f03a37b3d7470 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'dialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -32,7 +32,7 @@ class Ui_Dialog(object):
 
         self.label = QLabel(Dialog)
         self.label.setObjectName(u"label")
-        self.label.setAlignment(Qt.AlignCenter)
+        self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
         self.label.setWordWrap(True)
 
         self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
index e8e4d1ecdde12cf09dc34b41488183fcb50bbf3d..be733fc4bd33c1f74ad965c531a126391c661caa 100644 (file)
@@ -7,4 +7,3 @@ A Python application that demonstrates the analogous example in C++
 .. image:: mimetypesbrowser.png
    :width: 400
    :alt: mimetypebrowser screenshot
-
index 76404eeb70dea86e999681b053ab5dda73578df7..8842823f7d6df27194f9341f654b951497089cd6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from mimetypemodel import MimeTypeModel
 from PySide6.QtCore import (QDir, QFileInfo, QMimeDatabase, QModelIndex, Qt,
@@ -40,7 +41,7 @@ class MainWindow(QMainWindow):
         self.about_action = QAction(
             "About Qt",
             self,
-            shortcut=QKeySequence(QKeySequence.HelpContents),
+            shortcut=QKeySequence(QKeySequence.StandardKey.HelpContents),
             triggered=QApplication.aboutQt,
         )
 
@@ -63,7 +64,7 @@ class MainWindow(QMainWindow):
 
         self.items = self.m_model.findItems(
             "application/octet-stream",
-            Qt.MatchContains | Qt.MatchFixedString | Qt.MatchRecursive,
+            Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchRecursive
         )
 
         if self.items:
@@ -106,7 +107,7 @@ class MainWindow(QMainWindow):
         input_dialog = QInputDialog(self)
         input_dialog.setWindowTitle("Find")
         input_dialog.setLabelText("Text")
-        if input_dialog.exec() != QDialog.Accepted:
+        if input_dialog.exec() != QDialog.DialogCode.Accepted:
             return
 
         value = input_dialog.textValue().strip()
@@ -115,9 +116,9 @@ class MainWindow(QMainWindow):
 
         self.m_find_matches.clear()
         self.m_find_index = 0
-        items = self.m_model.findItems(
-            value, Qt.MatchContains | Qt.MatchFixedString | Qt.MatchRecursive
-        )
+        flags = (Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchFixedString
+                 | Qt.MatchFlag.MatchRecursive)
+        items = self.m_model.findItems(value, flags)
 
         for item in items:
             self.m_find_matches.append(self.m_model.indexFromItem(item))
@@ -157,5 +158,5 @@ class MainWindow(QMainWindow):
         self.m_find_previous_action.setEnabled(self.find_next_previous_enabled)
 
     def _select_and_goto(self, index: QModelIndex):
-        self.m_tree_view.scrollTo(index, QAbstractItemView.PositionAtCenter)
+        self.m_tree_view.scrollTo(index, QAbstractItemView.ScrollHint.PositionAtCenter)
         self.m_tree_view.setCurrentIndex(index)
index 5243f110ef22c850741659f4aacfeb4e4ff875fb..12df5c863f1f7330f880a38b8d26b2468da47ea7 100644 (file)
@@ -1,18 +1,17 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from typing import List
+from __future__ import annotations
 
 from PySide6.QtCore import QMimeDatabase, QMimeType, QModelIndex, QObject, Qt, qWarning
 from PySide6.QtGui import QStandardItem, QStandardItemModel
 
-mimeTypeRole = Qt.UserRole + 1
-iconQueriedRole = Qt.UserRole + 2
+mimeTypeRole = Qt.ItemDataRole.UserRole + 1
+iconQueriedRole = Qt.ItemDataRole.UserRole + 2
 
 
 def createRow(t: QMimeType):
     name_item = QStandardItem(t.name())
-    flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
+    flags = Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled
     name_item.setData(t, mimeTypeRole)
     name_item.setData(False, iconQueriedRole)
     name_item.setFlags(flags)
@@ -29,7 +28,7 @@ class MimeTypeModel(QStandardItemModel):
 
     def populate(self):
         mime_database = QMimeDatabase()
-        all_types: List[QMimeType] = mime_database.allMimeTypes()
+        all_types: list[QMimeType] = mime_database.allMimeTypes()
 
         # Move top level types to rear end of list, sort this partition,
         # create top level items and truncate the list.
index 4742a31b8824b347207be92049c9c598c353ee56..4f7b6959a5b905589226a8e97cd63e3f2cbc659b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the corelib/mimetypes/mimetypebrowser example from from Qt"""
 
index f87a2f4b572cb427a6afdfa70b6cd3f662c546ea..7da39a77c376f7db510dc07cc2ef77249d75714d 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/tools/settingseditor example from Qt v5.x"""
 
@@ -27,7 +28,7 @@ class TypeChecker:
     def __init__(self, parent=None):
         self.bool_exp = QRegularExpression('^(true)|(false)$')
         assert self.bool_exp.isValid()
-        self.bool_exp.setPatternOptions(QRegularExpression.CaseInsensitiveOption)
+        self.bool_exp.setPatternOptions(QRegularExpression.PatternOption.CaseInsensitiveOption)
 
         self.byteArray_exp = QRegularExpression(r'^[\x00-\xff]*$')
         assert self.byteArray_exp.isValid()
@@ -105,13 +106,13 @@ class TypeChecker:
                           min(int(match.captured(3)), 255),
                           min(int(match.captured(4)), 255))
         if isinstance(original_value, QDate):
-            value = QDate.fromString(text, Qt.ISODate)
+            value = QDate.fromString(text, Qt.DateFormat.ISODate)
             return value if value.isValid() else None
         if isinstance(original_value, QDateTime):
-            value = QDateTime.fromString(text, Qt.ISODate)
+            value = QDateTime.fromString(text, Qt.DateFormat.ISODate)
             return value if value.isValid() else None
         if isinstance(original_value, QTime):
-            value = QTime.fromString(text, Qt.ISODate)
+            value = QTime.fromString(text, Qt.DateFormat.ISODate)
             return value if value.isValid() else None
         if isinstance(original_value, QPoint):
             match = self.point_exp.match(text)
@@ -172,8 +173,8 @@ class MainWindow(QMainWindow):
             self.load_ini_file(file_name)
 
     def load_ini_file(self, file_name):
-        settings = QSettings(file_name, QSettings.IniFormat)
-        if settings.status() != QSettings.NoError:
+        settings = QSettings(file_name, QSettings.Format.IniFormat)
+        if settings.status() != QSettings.Status.NoError:
             return
         self.set_settings_object(settings)
         self.fallbacks_action.setEnabled(False)
@@ -185,7 +186,7 @@ class MainWindow(QMainWindow):
                                                    "Property List Files (*.plist)")
 
         if file_name:
-            settings = QSettings(file_name, QSettings.NativeFormat)
+            settings = QSettings(file_name, QSettings.Format.NativeFormat)
             self.set_settings_object(settings)
             self.fallbacks_action.setEnabled(False)
 
@@ -196,7 +197,7 @@ class MainWindow(QMainWindow):
                                         QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
 
         if ok and path != '':
-            settings = QSettings(path, QSettings.NativeFormat)
+            settings = QSettings(path, QSettings.Format.NativeFormat)
             self.set_settings_object(settings)
             self.fallbacks_action.setEnabled(False)
 
@@ -321,15 +322,17 @@ class LocationDialog(QDialog):
         self.locations_groupbox = QGroupBox("Setting Locations")
 
         self.locations_table = QTableWidget()
-        self.locations_table.setSelectionMode(QAbstractItemView.SingleSelection)
-        self.locations_table.setSelectionBehavior(QAbstractItemView.SelectRows)
-        self.locations_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
+        self.locations_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
+        self.locations_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
+        self.locations_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
         self.locations_table.setColumnCount(2)
         self.locations_table.setHorizontalHeaderLabels(("Location", "Access"))
-        self.locations_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
-        self.locations_table.horizontalHeader().resizeSection(1, 180)
+        header = self.locations_table.horizontalHeader()
+        header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
+        header.resizeSection(1, 180)
 
-        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+                                           | QDialogButtonBox.StandardButton.Cancel)
 
         self.format_combo.activated.connect(self.update_locations)
         self.scope_cCombo.activated.connect(self.update_locations)
@@ -360,15 +363,15 @@ class LocationDialog(QDialog):
 
     def format(self):
         if self.format_combo.currentIndex() == 0:
-            return QSettings.NativeFormat
+            return QSettings.Format.NativeFormat
         else:
-            return QSettings.IniFormat
+            return QSettings.Format.IniFormat
 
     def scope(self):
         if self.scope_cCombo.currentIndex() == 0:
-            return QSettings.UserScope
+            return QSettings.Scope.UserScope
         else:
-            return QSettings.SystemScope
+            return QSettings.Scope.SystemScope
 
     def organization(self):
         return self.organization_combo.currentText()
@@ -385,12 +388,12 @@ class LocationDialog(QDialog):
 
         for i in range(2):
             if i == 0:
-                if self.scope() == QSettings.SystemScope:
+                if self.scope() == QSettings.Scope.SystemScope:
                     continue
 
-                actual_scope = QSettings.UserScope
+                actual_scope = QSettings.Scope.UserScope
             else:
-                actual_scope = QSettings.SystemScope
+                actual_scope = QSettings.Scope.SystemScope
 
             for j in range(2):
                 if j == 0:
@@ -419,13 +422,13 @@ class LocationDialog(QDialog):
                         disable = False
                     else:
                         item1.setText("Read-only")
-                    self.button_box.button(QDialogButtonBox.Ok).setDisabled(disable)
+                    self.button_box.button(QDialogButtonBox.StandardButton.Ok).setDisabled(disable)
                 else:
                     item1.setText("Read-only fallback")
 
                 if disable:
-                    item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled)
-                    item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled)
+                    item0.setFlags(item0.flags() & ~Qt.ItemFlag.ItemIsEnabled)
+                    item1.setFlags(item1.flags() & ~Qt.ItemFlag.ItemIsEnabled)
 
                 self.locations_table.setItem(row, 0, item0)
                 self.locations_table.setItem(row, 1, item1)
@@ -441,8 +444,8 @@ class SettingsTree(QTreeWidget):
         self.setItemDelegate(VariantDelegate(self._type_checker, self))
 
         self.setHeaderLabels(("Setting", "Type", "Value"))
-        self.header().setSectionResizeMode(0, QHeaderView.Stretch)
-        self.header().setSectionResizeMode(2, QHeaderView.Stretch)
+        self.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
+        self.header().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
 
         self.settings = None
         self.refresh_timer = QTimer()
@@ -451,12 +454,12 @@ class SettingsTree(QTreeWidget):
 
         self.group_icon = QIcon()
         style = self.style()
-        self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirClosedIcon),
-                                  QIcon.Normal, QIcon.Off)
-        self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirOpenIcon),
-                                  QIcon.Normal, QIcon.On)
+        self.group_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirClosedIcon),
+                                  QIcon.Mode.Normal, QIcon.State.Off)
+        self.group_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirOpenIcon),
+                                  QIcon.Mode.Normal, QIcon.State.On)
         self.key_icon = QIcon()
-        self.key_icon.addPixmap(style.standardPixmap(QStyle.SP_FileIcon))
+        self.key_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_FileIcon))
 
         self.refresh_timer.timeout.connect(self.maybe_refresh)
 
@@ -514,7 +517,7 @@ class SettingsTree(QTreeWidget):
         self.itemChanged.connect(self.update_setting)
 
     def event(self, event):
-        if event.type() == QEvent.WindowActivate:
+        if event.type() == QEvent.Type.WindowActivate:
             if self.isActiveWindow() and self.auto_refresh:
                 self.maybe_refresh()
 
@@ -528,7 +531,7 @@ class SettingsTree(QTreeWidget):
             key = ancestor.text(0) + '/' + key
             ancestor = ancestor.parent()
 
-        self.settings.setValue(key, item.data(2, Qt.UserRole))
+        self.settings.setValue(key, item.data(2, Qt.ItemDataRole.UserRole))
 
         if self.auto_refresh:
             self.refresh()
@@ -542,7 +545,7 @@ class SettingsTree(QTreeWidget):
                 child = self.child_at(parent, child_index)
                 child.setText(1, '')
                 child.setText(2, '')
-                child.setData(2, Qt.UserRole, None)
+                child.setData(2, Qt.ItemDataRole.UserRole, None)
                 self.move_item_forward(parent, child_index, divider_index)
             else:
                 child = self.create_item(group, parent, divider_index)
@@ -580,7 +583,7 @@ class SettingsTree(QTreeWidget):
                         value = self.settings.value(key, type=value_type)
                 child.setText(1, value.__class__.__name__)
             child.setText(2, VariantDelegate.display_text(value))
-            child.setData(2, Qt.UserRole, value)
+            child.setData(2, Qt.ItemDataRole.UserRole, value)
 
         while divider_index < self.child_count(parent):
             self.delete_item(parent, divider_index)
@@ -597,7 +600,7 @@ class SettingsTree(QTreeWidget):
             item = QTreeWidgetItem(self, after)
 
         item.setText(0, text)
-        item.setFlags(item.flags() | Qt.ItemIsEditable)
+        item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable)
         return item
 
     def delete_item(self, parent, index):
@@ -637,7 +640,7 @@ class VariantDelegate(QItemDelegate):
 
     def paint(self, painter, option, index):
         if index.column() == 2:
-            value = index.model().data(index, Qt.UserRole)
+            value = index.model().data(index, Qt.ItemDataRole.UserRole)
             if not self.is_supported_type(value):
                 my_option = QStyleOptionViewItem(option)
                 my_option.state &= ~QStyle.State_Enabled
@@ -650,7 +653,7 @@ class VariantDelegate(QItemDelegate):
         if index.column() != 2:
             return None
 
-        original_value = index.model().data(index, Qt.UserRole)
+        original_value = index.model().data(index, Qt.ItemDataRole.UserRole)
         if not self.is_supported_type(original_value):
             return None
 
@@ -671,7 +674,7 @@ class VariantDelegate(QItemDelegate):
     def setEditorData(self, editor, index):
         if not editor:
             return
-        value = index.model().data(index, Qt.UserRole)
+        value = index.model().data(index, Qt.ItemDataRole.UserRole)
         if isinstance(editor, QCheckBox):
             editor.setCheckState(Qt.Checked if value else Qt.Unchecked)
         elif isinstance(editor, QSpinBox):
@@ -688,7 +691,7 @@ class VariantDelegate(QItemDelegate):
             state, text, _ = validator.validate(text, 0)
             if state != QValidator.Acceptable:
                 return None
-        original_value = index.model().data(index, Qt.UserRole)
+        original_value = index.model().data(index, Qt.ItemDataRole.UserRole)
         return self._type_checker.from_string(text, original_value)
 
     def setModelData(self, editor, model, index):
@@ -700,8 +703,8 @@ class VariantDelegate(QItemDelegate):
         else:
             value = self.value_from_lineedit(editor, model, index)
         if value is not None:
-            model.setData(index, value, Qt.UserRole)
-            model.setData(index, self.display_text(value), Qt.DisplayRole)
+            model.setData(index, value, Qt.ItemDataRole.UserRole)
+            model.setData(index, self.display_text(value), Qt.ItemDataRole.DisplayRole)
 
     @staticmethod
     def is_supported_type(value):
index 4689813d415e46bb7ea5f0570ee57bba4c260ccd..af53b343008e4faaf49f0c40860a69ab235c6c9d 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the corelib/threads/mandelbrot example from Qt v5.x, originating from PyQt"""
 
@@ -8,7 +9,7 @@ from argparse import ArgumentParser, RawTextHelpFormatter
 import sys
 
 from PySide6.QtCore import (Signal, QMutex, QElapsedTimer, QMutexLocker,
-                            QPoint, QPointF, QSize, Qt, QThread,
+                            QPoint, QPointF, QRectF, QSize, Qt, QThread,
                             QWaitCondition, Slot)
 from PySide6.QtGui import QColor, QImage, QPainter, QPixmap, qRgb
 from PySide6.QtWidgets import QApplication, QWidget
@@ -72,7 +73,7 @@ class RenderThread(QThread):
             self._result_size = resultSize
 
             if not self.isRunning():
-                self.start(QThread.LowPriority)
+                self.start(QThread.Priority.LowPriority)
             else:
                 self.restart = True
                 self.condition.wakeOne()
@@ -90,7 +91,7 @@ class RenderThread(QThread):
 
             half_width = resultSize.width() // 2
             half_height = resultSize.height() // 2
-            image = QImage(resultSize, QImage.Format_RGB32)
+            image = QImage(resultSize, QImage.Format.Format_RGB32)
 
             curpass = 0
 
@@ -193,7 +194,7 @@ class RenderThread(QThread):
         g = pow(g * s, 0.8)
         b = pow(b * s, 0.8)
 
-        return qRgb(r * 255, g * 255, b * 255)
+        return qRgb(int(r * 255), int(g * 255), int(b * 255))
 
 
 class MandelbrotWidget(QWidget):
@@ -213,16 +214,16 @@ class MandelbrotWidget(QWidget):
         self.thread.rendered_image.connect(self.update_pixmap)
 
         self.setWindowTitle("Mandelbrot")
-        self.setCursor(Qt.CrossCursor)
+        self.setCursor(Qt.CursorShape.CrossCursor)
         self._info = ''
 
     def paintEvent(self, event):
         with QPainter(self) as painter:
-            painter.fillRect(self.rect(), Qt.black)
+            painter.fillRect(self.rect(), Qt.GlobalColor.black)
 
             if self.pixmap.isNull():
-                painter.setPen(Qt.white)
-                painter.drawText(self.rect(), Qt.AlignCenter,
+                painter.setPen(Qt.GlobalColor.white)
+                painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter,
                                  "Rendering initial image, please wait...")
                 return
 
@@ -249,31 +250,33 @@ class MandelbrotWidget(QWidget):
             metrics = painter.fontMetrics()
             text_width = metrics.horizontalAdvance(text)
 
-            painter.setPen(Qt.NoPen)
+            painter.setPen(Qt.PenStyle.NoPen)
             painter.setBrush(QColor(0, 0, 0, 127))
-            painter.drawRect((self.width() - text_width) / 2 - 5, 0, text_width + 10,
-                             metrics.lineSpacing() + 5)
-            painter.setPen(Qt.white)
-            painter.drawText((self.width() - text_width) / 2,
-                             metrics.leading() + metrics.ascent(), text)
+            box = QRectF((self.width() - text_width) / 2 - 5, 0,
+                         text_width + 10, metrics.lineSpacing() + 5)
+            painter.drawRect(box)
+            painter.setPen(Qt.GlobalColor.white)
+            pos = QPointF((self.width() - text_width) / 2,
+                          metrics.leading() + metrics.ascent())
+            painter.drawText(pos, text)
 
     def resizeEvent(self, event):
         self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
 
     def keyPressEvent(self, event):
-        if event.key() == Qt.Key_Plus:
+        if event.key() == Qt.Key.Key_Plus:
             self.zoom(ZOOM_IN_FACTOR)
-        elif event.key() == Qt.Key_Minus:
+        elif event.key() == Qt.Key.Key_Minus:
             self.zoom(ZOOM_OUT_FACTOR)
-        elif event.key() == Qt.Key_Left:
+        elif event.key() == Qt.Key.Key_Left:
             self.scroll(-SCROLL_STEP, 0)
-        elif event.key() == Qt.Key_Right:
+        elif event.key() == Qt.Key.Key_Right:
             self.scroll(+SCROLL_STEP, 0)
-        elif event.key() == Qt.Key_Down:
+        elif event.key() == Qt.Key.Key_Down:
             self.scroll(0, -SCROLL_STEP)
-        elif event.key() == Qt.Key_Up:
+        elif event.key() == Qt.Key.Key_Up:
             self.scroll(0, +SCROLL_STEP)
-        elif event.key() == Qt.Key_Q:
+        elif event.key() == Qt.Key.Key_Q:
             self.close()
         else:
             super(MandelbrotWidget, self).keyPressEvent(event)
@@ -284,18 +287,18 @@ class MandelbrotWidget(QWidget):
         self.zoom(pow(ZOOM_IN_FACTOR, num_steps))
 
     def mousePressEvent(self, event):
-        if event.buttons() == Qt.LeftButton:
+        if event.buttons() == Qt.MouseButton.LeftButton:
             self._last_drag_pos = event.position()
 
     def mouseMoveEvent(self, event):
-        if event.buttons() & Qt.LeftButton:
+        if event.buttons() & Qt.MouseButton.LeftButton:
             pos = event.position()
             self._pixmap_offset += pos - self._last_drag_pos
             self._last_drag_pos = pos
             self.update()
 
     def mouseReleaseEvent(self, event):
-        if event.button() == Qt.LeftButton:
+        if event.button() == Qt.MouseButton.LeftButton:
             pos = event.position()
             self._pixmap_offset += pos - self._last_drag_pos
             self._last_drag_pos = QPointF()
index f1283fcbd2b464eda5e6089a2af2043eb148f390..587c91c376516b322c552a77a112e707ba330a05 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 QtDataVisualization example"""
 
index 7f721d4aa74428b1d5e375154b8fa723e7e13320..25f2e9b42dd45df84a3c891629fceb9fb3423525 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import Enum
 from math import sin, cos, degrees
index 7938a5ca153d49a77a6f6c4cdf999f0e9cd48657..6d72c6959b57a1f9c38fa76212051f27713c236f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from graphmodifier import GraphModifier
 
@@ -123,20 +124,20 @@ class BarGraph(QObject):
         reflectionCheckBox.setText("Show reflections")
         reflectionCheckBox.setChecked(False)
 
-        rotationSliderX = QSlider(Qt.Horizontal, self._barsWidget)
+        rotationSliderX = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
         rotationSliderX.setTickInterval(30)
         rotationSliderX.setTickPosition(QSlider.TicksBelow)
         rotationSliderX.setMinimum(-180)
         rotationSliderX.setValue(0)
         rotationSliderX.setMaximum(180)
-        rotationSliderY = QSlider(Qt.Horizontal, self._barsWidget)
+        rotationSliderY = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
         rotationSliderY.setTickInterval(15)
         rotationSliderY.setTickPosition(QSlider.TicksAbove)
         rotationSliderY.setMinimum(-90)
         rotationSliderY.setValue(0)
         rotationSliderY.setMaximum(90)
 
-        fontSizeSlider = QSlider(Qt.Horizontal, self._barsWidget)
+        fontSizeSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
         fontSizeSlider.setTickInterval(10)
         fontSizeSlider.setTickPosition(QSlider.TicksBelow)
         fontSizeSlider.setMinimum(1)
@@ -176,7 +177,7 @@ class BarGraph(QObject):
         axisTitlesFixedCB.setText("Axis titles fixed")
         axisTitlesFixedCB.setChecked(True)
 
-        axisLabelRotationSlider = QSlider(Qt.Horizontal, self._barsWidget)
+        axisLabelRotationSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
         axisLabelRotationSlider.setTickInterval(10)
         axisLabelRotationSlider.setTickPosition(QSlider.TicksBelow)
         axisLabelRotationSlider.setMinimum(0)
index 0402be60793449b28fae21615c4b9a05ea53628a..6c856bf1a6b389c5fe5619a6e207385c1c71fc61 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import Enum
 from math import sin, cos, degrees
index e8d94ee96b16d87624eedc8bceb0c8305f94cd3a..f79e3d2b75d870e99a5a24792a4efb7ecad14cf0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from math import atan, degrees
index b5495b4229c318a746bbbef2370484fdb2f0ff90..ea52799e09d65be79d1fd72dc2721a22649c288f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QPoint, Qt, Slot
 from PySide6.QtGui import QLinearGradient, QVector3D
index 4b57b85ddce934ea75acfe78b926194d8582f2ce..9eb87ff5fb0fe74ef56a5746b85aa54c87b3b119 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt DataVisualization graphgallery example from Qt v6.x"""
 
index 18d6de8d79b62bf5a44f0a362b2ef6afbdbaed2c..28b177910be52043b7e7809a5d360cb1cbf4d808 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 7c16ef7b951c88b2322ae2f7ab8b8b1fc737b0e3..52e27488af4faa0d03929ef073b35a11d437154a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from math import cos, degrees, sqrt
 
index ffcd428ee254e6d315ffac458df073a47345a631..14a690280c5011a7eea11584ee5491b39f909c51 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, QSize, Qt
 from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
index d42f496f905ab943f39e2a2582ec58b479c38815..1ecd0d4d51fe5f281a6c7edbb195a001edf9db7b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from surfacegraphmodifier import SurfaceGraphModifier
 
@@ -127,19 +128,19 @@ class SurfaceGraph(QObject):
         selectionVBox.addWidget(modeSliceColumnRB)
         selectionGroupBox.setLayout(selectionVBox)
         axisGroupBox = QGroupBox("Axis ranges")
-        axisMinSliderX = QSlider(Qt.Horizontal)
+        axisMinSliderX = QSlider(Qt.Orientation.Horizontal)
         axisMinSliderX.setMinimum(0)
         axisMinSliderX.setTickInterval(1)
         axisMinSliderX.setEnabled(True)
-        axisMaxSliderX = QSlider(Qt.Horizontal)
+        axisMaxSliderX = QSlider(Qt.Orientation.Horizontal)
         axisMaxSliderX.setMinimum(1)
         axisMaxSliderX.setTickInterval(1)
         axisMaxSliderX.setEnabled(True)
-        axisMinSliderZ = QSlider(Qt.Horizontal)
+        axisMinSliderZ = QSlider(Qt.Orientation.Horizontal)
         axisMinSliderZ.setMinimum(0)
         axisMinSliderZ.setTickInterval(1)
         axisMinSliderZ.setEnabled(True)
-        axisMaxSliderZ = QSlider(Qt.Horizontal)
+        axisMaxSliderZ = QSlider(Qt.Orientation.Horizontal)
         axisMaxSliderZ.setMinimum(1)
         axisMaxSliderZ.setTickInterval(1)
         axisMaxSliderZ.setEnabled(True)
index 68499fac878c6a5782b2cacdf41d747771aef30a..9cd0afeaef9cfc25e71057bfc5bb781b2c2b3869 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from math import sqrt, sin
index 350edcb4fc81929370d74a62123e49f20bbd0ac0..95995e3610d32ef73aff09fad9863fc75be790c0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt
 from PySide6.QtGui import QImage, QVector3D
index 50bdefa6afd17aadf4bdcf560ec6c50d1a737ced..5b1986b87a449ba4ffa385103c31c1f2e7603098 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Signal
 
index f69ebaf8049dd988a5c9df88b11c201cb518dc63..ad2b2a7cb1cb13cf0070e24df4fba4d447fc6238 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtDataVisualization import QBarDataProxy, QBarDataItem
index 752bc3887422abb671d560f95eefe2d016fd2ff0..c9b8ab1abfe64c55b524115f64cc5a2e9ea4e7b9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Signal
 
index db720dcededdac4a38007ab4d195e58f2e27c800..6b455bc421013cb492ff7e94a11d89d2ae3e2f89 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 import sys
@@ -23,7 +24,7 @@ if __name__ == '__main__':
     print(DESCRIPTION)
 
     surface = Q3DSurface()
-    surface.setFlags(surface.flags() ^ Qt.FramelessWindowHint)
+    surface.setFlags(surface.flags() ^ Qt.WindowType.FramelessWindowHint)
     axis = surface.axisX()
     axis.setTitle("X")
     axis.setTitleVisible(True)
index aca2a6ab834ff7609db5aef4bc84a181ce58ca49..05fa2183ad1ffa6f909bc4ec69754f0fc692b723 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from math import sin, pi
 
index 7b980bc1708a06916b44ad61b931a2132d48732b..edc41eaf5b446ce54e8a752037b0781955da0f27 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt DataVisualization qmlsurfacegallery example from Qt v6.x"""
 
@@ -31,7 +32,7 @@ if __name__ == "__main__":
 
     qml_file = Path(__file__).resolve().parent / "qml" / "qmlsurfacegallery" / "main.qml"
     viewer.setSource(QUrl.fromLocalFile(qml_file))
-    viewer.setResizeMode(QQuickView.SizeRootObjectToView)
+    viewer.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     viewer.show()
 
     ex = app.exec()
index f4397018470c16f37641455fee7d002bd5f0aa9e..7f91f1e92c2f5c84898f78c7451cbb136079b415 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -77,19 +78,19 @@ class Window(QWidget):
         selection_vbox.addWidget(mode_slice_column_rb)
         selection_group_box.setLayout(selection_vbox)
 
-        axis_min_slider_x = QSlider(Qt.Horizontal, self)
+        axis_min_slider_x = QSlider(Qt.Orientation.Horizontal, self)
         axis_min_slider_x.setMinimum(0)
         axis_min_slider_x.setTickInterval(1)
         axis_min_slider_x.setEnabled(True)
-        axis_max_slider_x = QSlider(Qt.Horizontal, self)
+        axis_max_slider_x = QSlider(Qt.Orientation.Horizontal, self)
         axis_max_slider_x.setMinimum(1)
         axis_max_slider_x.setTickInterval(1)
         axis_max_slider_x.setEnabled(True)
-        axis_min_slider_z = QSlider(Qt.Horizontal, self)
+        axis_min_slider_z = QSlider(Qt.Orientation.Horizontal, self)
         axis_min_slider_z.setMinimum(0)
         axis_min_slider_z.setTickInterval(1)
         axis_min_slider_z.setEnabled(True)
-        axis_max_slider_z = QSlider(Qt.Horizontal, self)
+        axis_max_slider_z = QSlider(Qt.Orientation.Horizontal, self)
         axis_max_slider_z.setMinimum(1)
         axis_max_slider_z.setTickInterval(1)
         axis_max_slider_z.setEnabled(True)
index 69eea716cc372cc9fa28022fe956313f881f2d52..26a910e8fd697095c85b6985a40532fd48a8f6fc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 from pathlib import Path
index f4397018470c16f37641455fee7d002bd5f0aa9e..7f91f1e92c2f5c84898f78c7451cbb136079b415 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -77,19 +78,19 @@ class Window(QWidget):
         selection_vbox.addWidget(mode_slice_column_rb)
         selection_group_box.setLayout(selection_vbox)
 
-        axis_min_slider_x = QSlider(Qt.Horizontal, self)
+        axis_min_slider_x = QSlider(Qt.Orientation.Horizontal, self)
         axis_min_slider_x.setMinimum(0)
         axis_min_slider_x.setTickInterval(1)
         axis_min_slider_x.setEnabled(True)
-        axis_max_slider_x = QSlider(Qt.Horizontal, self)
+        axis_max_slider_x = QSlider(Qt.Orientation.Horizontal, self)
         axis_max_slider_x.setMinimum(1)
         axis_max_slider_x.setTickInterval(1)
         axis_max_slider_x.setEnabled(True)
-        axis_min_slider_z = QSlider(Qt.Horizontal, self)
+        axis_min_slider_z = QSlider(Qt.Orientation.Horizontal, self)
         axis_min_slider_z.setMinimum(0)
         axis_min_slider_z.setTickInterval(1)
         axis_min_slider_z.setEnabled(True)
-        axis_max_slider_z = QSlider(Qt.Horizontal, self)
+        axis_max_slider_z = QSlider(Qt.Orientation.Horizontal, self)
         axis_max_slider_z.setMinimum(1)
         axis_max_slider_z.setTickInterval(1)
         axis_max_slider_z.setEnabled(True)
index c0c1acecf61c264ed944a7df7c46776a9b6dcb80..30b49b8a6055085bc0458d8b1e0fc1c7af65879c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import numpy as np
 import math
@@ -23,9 +24,9 @@ SAMPLE_MIN = -8.0
 SAMPLE_MAX = 8.0
 
 
-X_ROLE = Qt.UserRole + 1
-Y_ROLE = Qt.UserRole + 2
-Z_ROLE = Qt.UserRole + 3
+X_ROLE = Qt.ItemDataRole.UserRole + 1
+Y_ROLE = Qt.ItemDataRole.UserRole + 2
+Z_ROLE = Qt.ItemDataRole.UserRole + 3
 
 
 class SqrtSinModel(QAbstractTableModel):
@@ -64,7 +65,7 @@ class SqrtSinModel(QAbstractTableModel):
     def columnCount(self, index=QModelIndex()):
         return self._x.size
 
-    def data(self, index, role=Qt.DisplayRole):
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
         row = index.row()
         col = index.column()
         if role == X_ROLE:
index f4397018470c16f37641455fee7d002bd5f0aa9e..7f91f1e92c2f5c84898f78c7451cbb136079b415 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -77,19 +78,19 @@ class Window(QWidget):
         selection_vbox.addWidget(mode_slice_column_rb)
         selection_group_box.setLayout(selection_vbox)
 
-        axis_min_slider_x = QSlider(Qt.Horizontal, self)
+        axis_min_slider_x = QSlider(Qt.Orientation.Horizontal, self)
         axis_min_slider_x.setMinimum(0)
         axis_min_slider_x.setTickInterval(1)
         axis_min_slider_x.setEnabled(True)
-        axis_max_slider_x = QSlider(Qt.Horizontal, self)
+        axis_max_slider_x = QSlider(Qt.Orientation.Horizontal, self)
         axis_max_slider_x.setMinimum(1)
         axis_max_slider_x.setTickInterval(1)
         axis_max_slider_x.setEnabled(True)
-        axis_min_slider_z = QSlider(Qt.Horizontal, self)
+        axis_min_slider_z = QSlider(Qt.Orientation.Horizontal, self)
         axis_min_slider_z.setMinimum(0)
         axis_min_slider_z.setTickInterval(1)
         axis_min_slider_z.setEnabled(True)
-        axis_max_slider_z = QSlider(Qt.Horizontal, self)
+        axis_max_slider_z = QSlider(Qt.Orientation.Horizontal, self)
         axis_max_slider_z.setMinimum(1)
         axis_max_slider_z.setTickInterval(1)
         axis_max_slider_z.setEnabled(True)
index c9f6e75a986c300adcca625ecc02a01441a32961..654c9d05c47145499d79cf09455cdd8611fb5f98 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import numpy as np
 import math
diff --git a/examples/dbus/listnames/doc/listnames.rst b/examples/dbus/listnames/doc/listnames.rst
new file mode 100644 (file)
index 0000000..3cae6b9
--- /dev/null
@@ -0,0 +1,7 @@
+D-Bus List Names Example
+========================
+
+Shows how to access the D-Bus bus daemon service.
+
+List Names is a command-line example which shows how to access the Qt D-Bus bus
+daemon service. The example prints various information about the bus daemon service
index 2240a06d87f3251a7343e72ef3deace003a5e4c7..1c279f5d718ca20d9978764eb99e83a1a5cb8725 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the QtDBus listnames example from Qt v6.x"""
 
diff --git a/examples/dbus/pingpong/doc/pingpong.rst b/examples/dbus/pingpong/doc/pingpong.rst
new file mode 100644 (file)
index 0000000..1531920
--- /dev/null
@@ -0,0 +1,8 @@
+D-Bus Ping Pong Example
+=======================
+
+Demonstrates a simple message system using D-Bus.
+
+Ping Pong is a command-line example that demonstrates the basics of Qt D-Bus.
+A message is sent to another application and there is a confirmation of the
+message.
index d61f2549911a7c87c820c39023f69d9064da742d..2e652244157e12e5924c05164737e0272d8aa67f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the QtDBus pingpong example from Qt v6.x (ping client)"""
 
index 0dec6eda08422554445fd40891146971fd074671..371291f2a24588d3201c5f6ffe9b15e7cc8ef5a5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the QtDBus pingpong example from Qt v6.x (pong server)"""
 
index 3f3a7ed6a8263c92b8a352a2bca86d22da1b2c7b..49e6168fdd7a503cdaca1491b786c49d774aa7c1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject
 from PySide6.QtQml import QmlAnonymous
index b9139c2e2d272ab8357559814ea937fd10800df6..1090f4d06bad760846c0f166ff501b215a82ff82 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from functools import partial
index a249b9fa221c3b8031a086897f2bdd5e685ed5f0..f32c158053b7bd2ab296c3e2f22abc6b6ee0a660 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt RESTful API client demo from Qt v6.x"""
 
@@ -28,6 +29,6 @@ if __name__ == "__main__":
     if not engine.rootObjects():
         sys.exit(-1)
 
-    ex = app.exec()
+    exit_code = app.exec()
     del engine
-    sys.exit(ex)
+    sys.exit(exit_code)
index b7f036c4e333a68166027e1272eaac3eae39c1f4..db399a7dc68d404eb0a11c40069a9324e97b70f0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from dataclasses import dataclass
@@ -27,9 +28,9 @@ class ColorUser:
 
 @QmlElement
 class ColorUserModel (QAbstractListModel):
-    IdRole = Qt.UserRole + 1
-    EmailRole = Qt.UserRole + 2
-    AvatarRole = Qt.UserRole + 3
+    IdRole = Qt.ItemDataRole.UserRole + 1
+    EmailRole = Qt.ItemDataRole.UserRole + 2
+    AvatarRole = Qt.ItemDataRole.UserRole + 3
 
     def __init__(self, parent=None):
         super().__init__(parent)
@@ -86,10 +87,10 @@ class Color:
 
 @QmlElement
 class ColorModel (QAbstractListModel):
-    IdRole = Qt.UserRole + 1
-    ColorRole = Qt.UserRole + 2
-    NameRole = Qt.UserRole + 3
-    PantoneValueRole = Qt.UserRole + 4
+    IdRole = Qt.ItemDataRole.UserRole + 1
+    ColorRole = Qt.ItemDataRole.UserRole + 2
+    NameRole = Qt.ItemDataRole.UserRole + 3
+    PantoneValueRole = Qt.ItemDataRole.UserRole + 4
 
     def __init__(self, parent=None):
         super().__init__(parent)
index d334ecd039f2be7199305c33c9e737bd29f18414..852c50fe31ea9e110b3628e6161e5b4a3bef460e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Property, Signal, ClassInfo
 from PySide6.QtNetwork import (QNetworkAccessManager, QRestAccessManager,
index 4039fe9fbd76577d8179dc91541b4f66dd3b4770..2c07c10c6de56ce67b6e6796029e9d4c4c1a0c6b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject
 
@@ -147,10 +148,10 @@ class AbstractViewer(QObject):
         if not self.hasContent():
             self.statusMessage("No content to print.", type)
             return
-        printer = QPrinter(QPrinter.HighResolution)
+        printer = QPrinter(QPrinter.PrinterMode.HighResolution)
         dlg = QPrintDialog(printer, self.mainWindow())
         dlg.setWindowTitle("Print Document")
-        if dlg.exec() == QDialog.Accepted:
+        if dlg.exec() == QDialog.DialogCode.Accepted:
             self.printDocument(printer)
         else:
             self.statusMessage("Printing canceled!", type)
index 1e5a34ca0cfb0e39072d16eb84ef0a54e408bffd..0cdf5369234130777b541ffb578915baa38bedc4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import json
 
@@ -104,19 +105,20 @@ class JsonItemModel(QAbstractItemModel):
             return None
 
         item = self.itemFromIndex(index)
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             if index.column() == 0:
                 return item.key()
             if index.column() == 1:
                 return item.value()
-        elif role == Qt.EditRole:
+        elif role == Qt.ItemDataRole.EditRole:
             if index.column() == 1:
                 return item.value()
         return None
 
     def headerData(self, section, orientation, role):
         return (self._headers[section]
-                if role == Qt.DisplayRole and orientation == Qt.Horizontal else None)
+                if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal
+                else None)
 
     def index(self, row, column, parent=QModelIndex()):
         if not self.hasIndex(row, column, parent):
@@ -187,14 +189,14 @@ class JsonViewer(AbstractViewer):
         zoomInIcon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn)
         a = menu.addAction(zoomInIcon, "&+Expand all", self._tree.expandAll)
         tb.addAction(a)
-        a.setPriority(QAction.LowPriority)
-        a.setShortcut(QKeySequence.New)
+        a.setPriority(QAction.Priority.LowPriority)
+        a.setShortcut(QKeySequence.StandardKey.New)
 
         zoomOutIcon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomOut)
         a = menu.addAction(zoomOutIcon, "&-Collapse all", self._tree.collapseAll)
         tb.addAction(a)
-        a.setPriority(QAction.LowPriority)
-        a.setShortcut(QKeySequence.New)
+        a.setPriority(QAction.Priority.LowPriority)
+        a.setShortcut(QKeySequence.StandardKey.New)
 
         if not self._searchKey:
             self._searchKey = QLineEdit(tb)
@@ -217,13 +219,13 @@ class JsonViewer(AbstractViewer):
             index = self._tree.model().index(i, 0)
             self._toplevel.addItem(index.data())
             item = self._toplevel.item(i)
-            item.setData(Qt.UserRole, index)
+            item.setData(Qt.ItemDataRole.UserRole, index)
             item.setToolTip(f"Toplevel Item {i}")
 
         self._toplevel.setAcceptDrops(True)
         self._tree.setDragEnabled(True)
-        self._tree.setContextMenuPolicy(Qt.CustomContextMenu)
-        self._toplevel.setContextMenuPolicy(Qt.CustomContextMenu)
+        self._tree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
+        self._toplevel.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
 
         self._toplevel.itemClicked.connect(self.onTopLevelItemClicked)
         self._toplevel.itemDoubleClicked.connect(self.onTopLevelItemDoubleClicked)
@@ -258,7 +260,7 @@ class JsonViewer(AbstractViewer):
         self.disablePrinting()
         file_name = QDir.toNativeSeparators(self._file.fileName())
         type = "open"
-        self._file.open(QIODevice.ReadOnly)
+        self._file.open(QIODevice.OpenModeFlag.ReadOnly)
         self._text = self._file.readAll().data().decode("utf-8")
         self._file.close()
 
@@ -277,7 +279,7 @@ class JsonViewer(AbstractViewer):
         return self._tree.model() is not None
 
     def indexOf(self, item):
-        return QModelIndex(item.data(Qt.UserRole))
+        return QModelIndex(item.data(Qt.ItemDataRole.UserRole))
 
     @Slot(QListWidgetItem)
     def onTopLevelItemClicked(self, item):
@@ -359,14 +361,14 @@ class JsonViewer(AbstractViewer):
         if not index.isValid():
             return
 
-        item = QListWidgetItem(index.data(Qt.DisplayRole), self._toplevel)
-        item.setData(Qt.UserRole, index)
+        item = QListWidgetItem(index.data(Qt.ItemDataRole.DisplayRole), self._toplevel)
+        item.setData(Qt.ItemDataRole.UserRole, index)
 
         # Set a tooltip that shows where the item is located in the tree
         parent = index.parent()
-        tooltip = index.data(Qt.DisplayRole).toString()
+        tooltip = index.data(Qt.ItemDataRole.DisplayRole).toString()
         while parent.isValid():
-            tooltip = parent.data(Qt.DisplayRole).toString() + "." + tooltip
+            tooltip = parent.data(Qt.ItemDataRole.DisplayRole).toString() + "." + tooltip
             parent = parent.parent()
 
         item.setToolTip(tooltip)
index 2af373ef33dd3af1a45f4ff4a89e953792d80fbf..5159b9c3b269142342cdb13a40f63406a2045607 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt Document Viewer demo from Qt v6.x"""
 
index e9abd0bec150321aa02c5b31de498abc74e5772b..d72cca283769b983d84c8203c4ac758a084f0d32 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import (QDialog, QFileDialog, QMainWindow, QMessageBox)
 from PySide6.QtCore import (QDir, QFile, QFileInfo, QSettings, Slot)
@@ -66,7 +67,7 @@ class MainWindow(QMainWindow):
     def onActionOpenTriggered(self):
         fileDialog = QFileDialog(self, "Open Document",
                                  self._currentDir.absolutePath())
-        while (fileDialog.exec() == QDialog.Accepted
+        while (fileDialog.exec() == QDialog.DialogCode.Accepted
                and not self.openFile(fileDialog.selectedFiles()[0])):
             pass
 
index 482061fc7ea3220a01dd41fb37ae4c109b4ca926..3406d6ed50d0247f8e0502c2df625f7a080c02fd 100644 (file)
        <item>
         <widget class="QSplitter" name="splitter">
          <property name="orientation">
-          <enum>Qt::Horizontal</enum>
+          <enum>Qt::Orientation::Horizontal</enum>
          </property>
          <widget class="QTabWidget" name="tabWidget">
           <property name="tabPosition">
-           <enum>QTabWidget::West</enum>
+           <enum>QTabWidget::TabPosition::West</enum>
           </property>
           <property name="currentIndex">
            <number>0</number>
@@ -70,7 +70,7 @@
              <x>0</x>
              <y>0</y>
              <width>798</width>
-             <height>479</height>
+             <height>472</height>
             </rect>
            </property>
           </widget>
@@ -88,7 +88,7 @@
      <x>0</x>
      <y>0</y>
      <width>983</width>
-     <height>23</height>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="qtFileMenu">
   </action>
   <action name="actionQuit">
    <property name="icon">
-    <iconset theme="application-exit">
-     <normaloff>.</normaloff>.</iconset>
+    <iconset theme="application-exit"/>
    </property>
    <property name="text">
     <string>Quit</string>
index d5695e4d0aad09cfb2250712badb2159be882c78..a1f4e7447198b14834735070f1d3e670593fc89a 100644 (file)
@@ -1,10 +1,11 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from math import sqrt
 
 from PySide6.QtWidgets import (QListView, QTreeView)
-from PySide6.QtGui import QIcon, QPainter
+from PySide6.QtGui import QIcon, QKeySequence, QPainter
 from PySide6.QtCore import (QDir, QIODevice, QModelIndex,
                             QPointF, Slot)
 from PySide6.QtPrintSupport import QPrinter
@@ -60,20 +61,16 @@ class PdfViewer(AbstractViewer):
         self._toolBar.addSeparator()
         self._toolBar.addWidget(self._zoomSelector)
 
-        actionZoomIn = self._toolBar.addAction("Zoom in")
-        actionZoomIn.setToolTip("Increase zoom level")
         icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn,
                                QIcon(":/demos/documentviewer/images/zoom-in.png"))
-        actionZoomIn.setIcon(icon)
-        self._toolBar.addAction(actionZoomIn)
+        actionZoomIn = self._toolBar.addAction(icon, "Zoom in", QKeySequence.StandardKey.ZoomIn)
+        actionZoomIn.setToolTip("Increase zoom level")
         actionZoomIn.triggered.connect(self.onActionZoomInTriggered)
 
-        actionZoomOut = self._toolBar.addAction("Zoom out")
-        actionZoomOut.setToolTip("Decrease zoom level")
         icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn,
                                QIcon(":/demos/documentviewer/images/zoom-out.png"))
-        actionZoomOut.setIcon(icon)
-        self._toolBar.addAction(actionZoomOut)
+        actionZoomOut = self._toolBar.addAction(icon, "Zoom out", QKeySequence.StandardKey.ZoomOut)
+        actionZoomOut.setToolTip("Decrease zoom level")
         actionZoomOut.triggered.connect(self.onActionZoomOutTriggered)
 
         nav.backAvailableChanged.connect(self._actionBack.setEnabled)
@@ -129,7 +126,7 @@ class PdfViewer(AbstractViewer):
     def openPdfFile(self):
         self.disablePrinting()
 
-        if self._file.open(QIODevice.ReadOnly):
+        if self._file.open(QIODevice.OpenModeFlag.ReadOnly):
             self._document.load(self._file)
 
         documentTitle = self._document.metaData(QPdfDocument.MetaDataField.Title)
index e5da5708f73d1a1c26041e8b5725660c906767af..70133d9985f301f7d16f700def2f88f2b9965594 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QComboBox
 from PySide6.QtCore import Signal, Slot
index 36c7dc9f329ee2f24e63fdae713cb37f14a5331b..be5ffaf450164149aaa0e23c967e1e9d02a2149d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QMenu
 from PySide6.QtCore import Signal, Slot
index fdfc56d408b387ee490f323b2e7c4b4f9ebe70f1..3f6e5e7a554a75b76bc9af6f66be5ed9d21dda9f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import Enum, auto
 
index ef5a38fcde9a5a3867056986bdc4fa970ca58d56..31754eae2df236db1bc67309fe3c5729d12f1f01 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import (QDialog, QFileDialog,
                                QPlainTextEdit)
@@ -31,7 +32,7 @@ class TxtViewer(AbstractViewer):
         cutIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut,
                                   QIcon(":/demos/documentviewer/images/cut.png"))
         cutAct = QAction(cutIcon, "Cut", self)
-        cutAct.setShortcuts(QKeySequence.Cut)
+        cutAct.setShortcuts(QKeySequence.StandardKey.Cut)
         cutAct.setStatusTip("Cut the current selection's contents to the clipboard")
         cutAct.triggered.connect(self._textEdit.cut)
         editMenu.addAction(cutAct)
@@ -40,7 +41,7 @@ class TxtViewer(AbstractViewer):
         copyIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy,
                                    QIcon(":/demos/documentviewer/images/copy.png"))
         copyAct = QAction(copyIcon, "Copy", self)
-        copyAct.setShortcuts(QKeySequence.Copy)
+        copyAct.setShortcuts(QKeySequence.StandardKey.Copy)
         copyAct.setStatusTip("Copy the current selection's contents to the clipboard")
         copyAct.triggered.connect(self._textEdit.copy)
         editMenu.addAction(copyAct)
@@ -49,7 +50,7 @@ class TxtViewer(AbstractViewer):
         pasteIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste,
                                     QIcon(":/demos/documentviewer/images/paste.png"))
         pasteAct = QAction(pasteIcon, "Paste", self)
-        pasteAct.setShortcuts(QKeySequence.Paste)
+        pasteAct.setShortcuts(QKeySequence.StandardKey.Paste)
         pasteAct.setStatusTip("Paste the clipboard's contents into the current selection")
         pasteAct.triggered.connect(self._textEdit.paste)
         editMenu.addAction(pasteAct)
@@ -87,13 +88,14 @@ class TxtViewer(AbstractViewer):
     def openFile(self):
         type = "open"
         file_name = QDir.toNativeSeparators(self._file.fileName())
-        if not self._file.open(QFile.ReadOnly | QFile.Text):
+        if not self._file.open(QFile.OpenModeFlag.ReadOnly
+                               | QFile.OpenModeFlag.Text):
             err = self._file.errorString()
             self.statusMessage(f"Cannot read file {file_name}:\n{err}.", type)
             return
 
         in_str = QTextStream(self._file)
-        QGuiApplication.setOverrideCursor(Qt.WaitCursor)
+        QGuiApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
         if self._textEdit.toPlainText():
             self._textEdit.clear()
             self.disablePrinting()
@@ -116,8 +118,8 @@ class TxtViewer(AbstractViewer):
     def saveFile(self, file):
         file_name = QDir.toNativeSeparators(self._file.fileName())
         errorMessage = ""
-        QGuiApplication.setOverrideCursor(Qt.WaitCursor)
-        if file.open(QFile.WriteOnly | QFile.Text):
+        QGuiApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
+        if file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
             out = QTextStream(file)
             out << self._textEdit.toPlainText()
         else:
@@ -135,8 +137,8 @@ class TxtViewer(AbstractViewer):
     def saveDocumentAs(self):
         dialog = QFileDialog(self.mainWindow())
         dialog.setWindowModality(Qt.WindowModal)
-        dialog.setAcceptMode(QFileDialog.AcceptSave)
-        if dialog.exec() != QDialog.Accepted:
+        dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
+        if dialog.exec() != QDialog.DialogCode.Accepted:
             return False
 
         files = dialog.selectedFiles()
index 5773ae5b8d21dcd7db8ce98cda8494307f123fe9..3c41004727266b22ea54942fb235d1f0060a7d47 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'mainwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -27,12 +27,12 @@ class Ui_MainWindow(object):
             MainWindow.setObjectName(u"MainWindow")
         MainWindow.resize(983, 602)
         icon = QIcon()
-        icon.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         MainWindow.setWindowIcon(icon)
         self.actionOpen = QAction(MainWindow)
         self.actionOpen.setObjectName(u"actionOpen")
         icon1 = QIcon()
-        icon1.addFile(u":/demos/documentviewer/images/open.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon1.addFile(u":/demos/documentviewer/images/open.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionOpen.setIcon(icon1)
         self.actionAbout = QAction(MainWindow)
         self.actionAbout.setObjectName(u"actionAbout")
@@ -41,18 +41,18 @@ class Ui_MainWindow(object):
         if QIcon.hasThemeIcon(iconThemeName):
             icon2 = QIcon.fromTheme(iconThemeName)
         else:
-            icon2.addFile(u":/demos/documentviewer/images/help-about.svgz", QSize(), QIcon.Normal, QIcon.Off)
+            icon2.addFile(u":/demos/documentviewer/images/help-about.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
 
         self.actionAbout.setIcon(icon2)
         self.actionForward = QAction(MainWindow)
         self.actionForward.setObjectName(u"actionForward")
         icon3 = QIcon()
-        icon3.addFile(u":/demos/documentviewer/images/go-next.svgz", QSize(), QIcon.Normal, QIcon.Off)
+        icon3.addFile(u":/demos/documentviewer/images/go-next.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionForward.setIcon(icon3)
         self.actionBack = QAction(MainWindow)
         self.actionBack.setObjectName(u"actionBack")
         icon4 = QIcon()
-        icon4.addFile(u":/demos/documentviewer/images/go-previous.svgz", QSize(), QIcon.Normal, QIcon.Off)
+        icon4.addFile(u":/demos/documentviewer/images/go-previous.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionBack.setIcon(icon4)
         self.actionPrint = QAction(MainWindow)
         self.actionPrint.setObjectName(u"actionPrint")
@@ -62,29 +62,23 @@ class Ui_MainWindow(object):
         if QIcon.hasThemeIcon(iconThemeName):
             icon5 = QIcon.fromTheme(iconThemeName)
         else:
-            icon5.addFile(u":/demos/documentviewer/images/print2x.png", QSize(), QIcon.Normal, QIcon.Off)
+            icon5.addFile(u":/demos/documentviewer/images/print2x.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
 
         self.actionPrint.setIcon(icon5)
         self.actionAboutQt = QAction(MainWindow)
         self.actionAboutQt.setObjectName(u"actionAboutQt")
         icon6 = QIcon()
-        icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Normal, QIcon.Off)
-        icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Normal, QIcon.On)
+        icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
+        icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.On)
         self.actionAboutQt.setIcon(icon6)
         self.actionRecent = QAction(MainWindow)
         self.actionRecent.setObjectName(u"actionRecent")
         icon7 = QIcon()
-        icon7.addFile(u":/demos/documentviewer/images/document-open-recent.svgz", QSize(), QIcon.Normal, QIcon.Off)
+        icon7.addFile(u":/demos/documentviewer/images/document-open-recent.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionRecent.setIcon(icon7)
         self.actionQuit = QAction(MainWindow)
         self.actionQuit.setObjectName(u"actionQuit")
-        icon8 = QIcon()
-        iconThemeName = u"application-exit"
-        if QIcon.hasThemeIcon(iconThemeName):
-            icon8 = QIcon.fromTheme(iconThemeName)
-        else:
-            icon8.addFile(u".", QSize(), QIcon.Normal, QIcon.Off)
-
+        icon8 = QIcon(QIcon.fromTheme(u"application-exit"))
         self.actionQuit.setIcon(icon8)
         self.centralwidget = QWidget(MainWindow)
         self.centralwidget.setObjectName(u"centralwidget")
@@ -97,10 +91,10 @@ class Ui_MainWindow(object):
         self.verticalLayout_2.setObjectName(u"verticalLayout_2")
         self.splitter = QSplitter(self.viewArea)
         self.splitter.setObjectName(u"splitter")
-        self.splitter.setOrientation(Qt.Horizontal)
+        self.splitter.setOrientation(Qt.Orientation.Horizontal)
         self.tabWidget = QTabWidget(self.splitter)
         self.tabWidget.setObjectName(u"tabWidget")
-        self.tabWidget.setTabPosition(QTabWidget.West)
+        self.tabWidget.setTabPosition(QTabWidget.TabPosition.West)
         self.bookmarkTab = QWidget()
         self.bookmarkTab.setObjectName(u"bookmarkTab")
         self.tabWidget.addTab(self.bookmarkTab, "")
@@ -119,7 +113,7 @@ class Ui_MainWindow(object):
         self.scrollArea.setWidgetResizable(True)
         self.scrollAreaWidgetContents = QWidget()
         self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents")
-        self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 798, 479))
+        self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 798, 472))
         self.scrollArea.setWidget(self.scrollAreaWidgetContents)
         self.splitter.addWidget(self.scrollArea)
 
@@ -131,7 +125,7 @@ class Ui_MainWindow(object):
         MainWindow.setCentralWidget(self.centralwidget)
         self.menubar = QMenuBar(MainWindow)
         self.menubar.setObjectName(u"menubar")
-        self.menubar.setGeometry(QRect(0, 0, 983, 23))
+        self.menubar.setGeometry(QRect(0, 0, 983, 26))
         self.qtFileMenu = QMenu(self.menubar)
         self.qtFileMenu.setObjectName(u"qtFileMenu")
         self.menuHelp = QMenu(self.menubar)
index ecae6770bf17f1822107a8f38ec60d4d47102b79..0d32cbfeb159304573cd3955bb2a83a79160b0a0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import Enum, auto
 
diff --git a/examples/demos/osmbuildings/OSMBuildings/Main.qml b/examples/demos/osmbuildings/OSMBuildings/Main.qml
new file mode 100644 (file)
index 0000000..aa0f65a
--- /dev/null
@@ -0,0 +1,233 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtQuick3D
+import QtQuick3D.Helpers
+
+import OSMBuildings
+
+Window {
+    width: 1024
+    height: 768
+    visible: true
+    title: qsTr("OSM Buildings")
+
+    OSMManager {
+        id: osmManager
+
+        onMapsDataReady: function( mapData, tileX, tileY, zoomLevel ){
+            mapModels.addModel(mapData, tileX, tileY, zoomLevel)
+        }
+    }
+
+    Component {
+        id: chunkModelMap
+        Node {
+            property variant mapData: null
+            property int tileX: 0
+            property int tileY: 0
+            property int zoomLevel: 0
+            Model {
+                id: basePlane
+                position: Qt.vector3d( osmManager.tileSizeX * tileX, osmManager.tileSizeY * -tileY, 0.0 )
+                scale: Qt.vector3d( osmManager.tileSizeX / 100., osmManager.tileSizeY / 100., 0.5)
+                source: "#Rectangle"
+                materials: [
+                    CustomMaterial {
+                        property TextureInput tileTexture: TextureInput {
+                            enabled: true
+                            texture: Texture {
+                                textureData: CustomTextureData {
+                                    Component.onCompleted: setImageData( mapData )
+                                } }
+                        }
+                        shadingMode: CustomMaterial.Shaded
+                        cullMode: Material.BackFaceCulling
+                        fragmentShader: "customshadertiles.frag"
+                    }
+                ]
+            }
+        }
+    }
+
+
+    View3D {
+        id: v3d
+        anchors.fill: parent
+
+        environment: ExtendedSceneEnvironment {
+            id: env
+            backgroundMode: SceneEnvironment.Color
+            clearColor: "#8099b3"
+            fxaaEnabled: true
+            fog: Fog {
+                id: theFog
+                color:"#8099b3"
+                enabled: true
+                depthEnabled: true
+                depthFar: 600
+            }
+        }
+
+        Node {
+            id: originNode
+            eulerRotation: Qt.vector3d(50.0, 0.0, 0.0)
+            PerspectiveCamera {
+                id: cameraNode
+                frustumCullingEnabled: true
+                clipFar: 600
+                clipNear: 100
+                fieldOfView: 90
+                z: 100
+
+                onZChanged: originNode.updateManagerCamera()
+
+            }
+            Component.onCompleted: updateManagerCamera()
+
+            onPositionChanged: updateManagerCamera()
+
+            onRotationChanged: updateManagerCamera()
+
+            function updateManagerCamera(){
+                osmManager.setCameraProperties( originNode.position,
+                                               originNode.right, cameraNode.z,
+                                               cameraController.minimumZoom,
+                                               cameraController.maximumZoom,
+                                               originNode.eulerRotation.x,
+                                               cameraController.minimumTilt,
+                                               cameraController.maximumTilt )
+            }
+        }
+
+        DirectionalLight {
+            color: Qt.rgba(1.0, 1.0, 0.95, 1.0)
+            ambientColor: Qt.rgba(0.5, 0.45, 0.45, 1.0)
+            rotation: Quaternion.fromEulerAngles(-10, -45, 0)
+        }
+
+        Node {
+            id: mapModels
+
+            function addModel(mapData, tileX, tileY, zoomLevel)
+            {
+                chunkModelMap.createObject( mapModels, { "mapData": mapData,
+                                               "tileX": tileX,
+                                               "tileY": tileY,
+                                               "zoomLevel": zoomLevel
+                                           } )
+            }
+        }
+
+        OSMCameraController {
+            id: cameraController
+            origin: originNode
+            camera: cameraNode
+        }
+    }
+
+    Item {
+        id: tokenArea
+        anchors.left: parent.left
+        anchors.bottom: parent.bottom
+        anchors.margins: 10
+        Text {
+            id: tokenInputArea
+            visible: false
+            anchors.left: parent.left
+            anchors.bottom: parent.bottom
+            color: "white"
+            styleColor: "black"
+            style: Text.Outline
+            text: "Open street map tile token: "
+            Rectangle {
+                border.width: 1
+                border.color: "black"
+                anchors.fill: tokenTxtInput
+                anchors.rightMargin: -30
+                Text {
+                    anchors.right: parent.right
+                    anchors.top: parent.top
+                    anchors.topMargin: 2
+                    anchors.rightMargin: 8
+                    color: "blue"
+                    styleColor: "white"
+                    style: Text.Outline
+                    text: "OK"
+                    Behavior on scale {
+                        NumberAnimation {
+                            easing.type: Easing.OutBack
+                        }
+                    }
+                    MouseArea {
+                        anchors.fill: parent
+                        anchors.margins: -10
+                        onPressedChanged: {
+                            if (pressed)
+                                parent.scale = 0.9
+                            else
+                                parent.scale = 1.0
+                        }
+                        onClicked: {
+                            tokenInputArea.visible = false
+                            osmManager.setToken(tokenTxtInput.text)
+                            tokenWarning.demoToken = osmManager.isDemoToken()
+                            tokenWarning.visible = true
+                        }
+                    }
+                }
+            }
+            TextInput {
+                id: tokenTxtInput
+                clip: true
+                anchors.left: parent.right
+                anchors.bottom: parent.bottom
+                anchors.bottomMargin: -3
+                height: tokenTxtInput.contentHeight + 5
+                width: 110
+                leftPadding: 5
+                rightPadding: 5
+            }
+        }
+
+        Text {
+            id: tokenWarning
+            property bool demoToken: true
+            anchors.left: parent.left
+            anchors.bottom: parent.bottom
+            color: "white"
+            styleColor: "black"
+            style: Text.Outline
+            text: demoToken ? "You are using the OSM limited demo token " :
+                              "You are using a token "
+            Text {
+                anchors.left: parent.right
+                color: "blue"
+                styleColor: "white"
+                style: Text.Outline
+                text: "click here to change"
+                Behavior on scale {
+                    NumberAnimation {
+                        easing.type: Easing.OutBack
+                    }
+                }
+                MouseArea {
+                    anchors.fill: parent
+                    onPressedChanged: {
+                        if (pressed)
+                            parent.scale = 0.9
+                        else
+                            parent.scale = 1.0
+                    }
+                    onClicked: {
+                        tokenWarning.visible = false
+                        tokenTxtInput.text = osmManager.token()
+                        tokenInputArea.visible = true
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/examples/demos/osmbuildings/OSMBuildings/OSMCameraController.qml b/examples/demos/osmbuildings/OSMBuildings/OSMCameraController.qml
new file mode 100644 (file)
index 0000000..114a6de
--- /dev/null
@@ -0,0 +1,234 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick3D
+
+Item {
+    id: root
+    required property Node origin
+    required property Camera camera
+
+    property real xSpeed: 0.05
+    property real ySpeed: 0.05
+
+    property bool xInvert: false
+    property bool yInvert: false
+
+    property bool mouseEnabled: true
+    property bool panEnabled: true
+
+    readonly property bool inputsNeedProcessing: status.useMouse || status.isPanning
+
+    readonly property real minimumZoom: 30
+    readonly property real maximumZoom: 200
+
+    readonly property real minimumTilt: 0
+    readonly property real maximumTilt: 80
+
+    implicitWidth: parent.width
+    implicitHeight: parent.height
+
+    Connections {
+        target: camera
+        Component.onCompleted: {
+            onZChanged()
+        }
+
+        function onZChanged() {
+            // Adjust near/far values based on distance
+            let distance = camera.z
+            if (distance < 1) {
+                camera.clipNear = 0.01
+                camera.clipFar = 100
+            } else if (distance < 100) {
+                camera.clipNear = 0.1
+                camera.clipFar = 1000
+            } else {
+                camera.clipNear = 1
+                camera.clipFar = 10000
+            }
+        }
+    }
+
+    DragHandler {
+        id: dragHandler
+        target: null
+        enabled: mouseEnabled
+        acceptedModifiers: Qt.NoModifier
+        acceptedButtons: Qt.RightButton
+        onCentroidChanged: {
+            mouseMoved(Qt.vector2d(centroid.position.x, centroid.position.y), false);
+        }
+
+        onActiveChanged: {
+            if (active)
+                mousePressed(Qt.vector2d(centroid.position.x, centroid.position.y));
+            else
+                mouseReleased(Qt.vector2d(centroid.position.x, centroid.position.y));
+        }
+    }
+
+    DragHandler {
+        id: ctrlDragHandler
+        target: null
+        enabled: mouseEnabled && panEnabled
+        //acceptedModifiers: Qt.ControlModifier
+        onCentroidChanged: {
+            panEvent(Qt.vector2d(centroid.position.x, centroid.position.y));
+        }
+
+        onActiveChanged: {
+            if (active)
+                startPan(Qt.vector2d(centroid.position.x, centroid.position.y));
+            else
+                endPan();
+        }
+    }
+
+    PinchHandler {
+        id: pinchHandler
+        target: null
+        enabled: mouseEnabled
+
+        property real distance: 0.0
+        onCentroidChanged: {
+            panEvent(Qt.vector2d(centroid.position.x, centroid.position.y))
+        }
+
+        onActiveChanged: {
+            if (active) {
+                startPan(Qt.vector2d(centroid.position.x, centroid.position.y))
+                distance = root.camera.z
+            } else {
+                endPan()
+                distance = 0.0
+            }
+        }
+        onScaleChanged: {
+
+            camera.z = distance * (1 / scale)
+            camera.z = Math.min(Math.max(camera.z, minimumZoom), maximumZoom)
+        }
+    }
+
+    TapHandler {
+        onTapped: root.forceActiveFocus()
+    }
+
+    WheelHandler {
+        id: wheelHandler
+        orientation: Qt.Vertical
+        target: null
+        enabled: mouseEnabled
+        onWheel: event => {
+            let delta = -event.angleDelta.y * 0.01;
+            camera.z += camera.z * 0.1 * delta
+            camera.z = Math.min(Math.max(camera.z, minimumZoom), maximumZoom)
+        }
+    }
+
+    function mousePressed(newPos) {
+        root.forceActiveFocus()
+        status.currentPos = newPos
+        status.lastPos = newPos
+        status.useMouse = true;
+    }
+
+    function mouseReleased(newPos) {
+        status.useMouse = false;
+    }
+
+    function mouseMoved(newPos: vector2d) {
+        status.currentPos = newPos;
+    }
+
+    function startPan(pos: vector2d) {
+        status.isPanning = true;
+        status.currentPanPos = pos;
+        status.lastPanPos = pos;
+    }
+
+    function endPan() {
+        status.isPanning = false;
+    }
+
+    function panEvent(newPos: vector2d) {
+        status.currentPanPos = newPos;
+    }
+
+    FrameAnimation {
+        id: updateTimer
+        running: root.inputsNeedProcessing
+        onTriggered: status.processInput(frameTime * 100)
+    }
+
+    QtObject {
+        id: status
+
+        property bool useMouse: false
+        property bool isPanning: false
+
+        property vector2d lastPos: Qt.vector2d(0, 0)
+        property vector2d lastPanPos: Qt.vector2d(0, 0)
+        property vector2d currentPos: Qt.vector2d(0, 0)
+        property vector2d currentPanPos: Qt.vector2d(0, 0)
+
+        property real rotateAlongZ: 0
+        property real rotateAlongXY: 50.0
+
+        function processInput(frameDelta) {
+            if (useMouse) {
+                // Get the delta
+                var delta = Qt.vector2d(lastPos.x - currentPos.x,
+                                        lastPos.y - currentPos.y);
+
+                var rotateX = delta.x * xSpeed * frameDelta
+                if ( xInvert )
+                    rotateX = -rotateX
+                rotateAlongZ += rotateX;
+                let rotateAlongZRad = rotateAlongZ * (Math.PI / 180.)
+
+                origin.rotate(rotateX, Qt.vector3d(0.0, 0.0, -1.0), Node.SceneSpace)
+
+                var rotateY = delta.y * -ySpeed * frameDelta
+                if ( yInvert )
+                    rotateY = -rotateY;
+
+                let preRotateAlongXY = rotateAlongXY + rotateY
+                if ( preRotateAlongXY <= maximumTilt && preRotateAlongXY >= minimumTilt )
+                {
+                    rotateAlongXY = preRotateAlongXY
+                    origin.rotate(rotateY, Qt.vector3d(Math.cos(rotateAlongZRad), Math.sin(-rotateAlongZRad), 0.0), Node.SceneSpace)
+                }
+
+                lastPos = currentPos;
+            }
+
+            if (isPanning) {
+                let delta = currentPanPos.minus(lastPanPos);
+                delta.x = -delta.x
+
+                delta.x = (delta.x / root.width) * camera.z * frameDelta
+                delta.y = (delta.y / root.height) * camera.z * frameDelta
+
+                let velocity = Qt.vector3d(0, 0, 0)
+                // X Movement
+                let xDirection = origin.right
+                velocity = velocity.plus(Qt.vector3d(xDirection.x * delta.x,
+                                                     xDirection.y * delta.x,
+                                                     xDirection.z * delta.x));
+                // Z Movement
+                let zDirection = origin.right.crossProduct(Qt.vector3d(0.0, 0.0, -1.0))
+                velocity = velocity.plus(Qt.vector3d(zDirection.x * delta.y,
+                                                     zDirection.y * delta.y,
+                                                     zDirection.z * delta.y));
+
+                origin.position = origin.position.plus(velocity)
+
+                lastPanPos = currentPanPos
+            }
+        }
+    }
+
+}
diff --git a/examples/demos/osmbuildings/OSMBuildings/customshadertiles.frag b/examples/demos/osmbuildings/OSMBuildings/customshadertiles.frag
new file mode 100644 (file)
index 0000000..67c32bf
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+void MAIN() {
+    vec2 tc = UV0;
+    BASE_COLOR = vec4( texture(tileTexture, vec2(tc.x, 1.0 - tc.y )).xyz, 1.0 );
+    ROUGHNESS = 0.3;
+    METALNESS = 0.0;
+    FRESNEL_POWER = 1.0;
+}
diff --git a/examples/demos/osmbuildings/OSMBuildings/qmldir b/examples/demos/osmbuildings/OSMBuildings/qmldir
new file mode 100644 (file)
index 0000000..02ff1ac
--- /dev/null
@@ -0,0 +1,3 @@
+module OSMBuildings
+Main 1.0 Main.qml
+OSMCameraController 1.0 OSMCameraController.qml
diff --git a/examples/demos/osmbuildings/doc/osmbuildings.rst b/examples/demos/osmbuildings/doc/osmbuildings.rst
new file mode 100644 (file)
index 0000000..487eb1d
--- /dev/null
@@ -0,0 +1,53 @@
+OSM Buildings
+=============
+
+
+This application shows a map obtained from OpenStreetMap (OSM) servers or a
+locally limited data set when the server is unavailable using
+:mod:`Qt Quick 3D <PySide6.QtQuick3D>`.
+
+It is a subset of the equivalent C++ demo, which in addition displays
+buildings. This functionality requires a special license key, though.
+
+Queue handling
+--------------
+
+The application uses a queue to handle concurrent requests to boost up the
+loading process of maps and building data.
+
+Fetching and parsing data
+-------------------------
+
+A custom request handler class is implemented for fetching the data from the
+OSM map servers.
+
+The downloaded ``PNG`` data is sent to a custom ``QQuick3DTextureData`` item to
+convert the ``PNG`` format to a texture for map tiles.
+
+The application uses camera position, orientation, zoom level, and tilt to find
+the nearest tiles in the view.
+
+Controls
+--------
+
+When you run the application, use the following controls for navigation.
+
++---------+---------------------------+---------+
+|         | Windows                   | Android |
++---------+---------------------------+---------+
+| Pan     | Left mouse button + drag  | Drag    |
++---------+---------------------------+---------+
+| Zoom    | Mouse wheel               | Pinch   |
++---------+---------------------------+---------+
+| Rotate  | Right mouse button + drag | n/a     |
++---------+---------------------------+---------+
+
+Rendering
+---------
+
+Every chunk of the map tile consists of a QML model (the 3D geometry) and a
+custom material which uses a rectangle as a base to render the tilemap texture.
+
+.. image:: osmbuildings.webp
+   :align: center
+   :alt: OSM Buildings Demo
diff --git a/examples/demos/osmbuildings/doc/osmbuildings.webp b/examples/demos/osmbuildings/doc/osmbuildings.webp
new file mode 100644 (file)
index 0000000..e9cfc82
Binary files /dev/null and b/examples/demos/osmbuildings/doc/osmbuildings.webp differ
diff --git a/examples/demos/osmbuildings/main.py b/examples/demos/osmbuildings/main.py
new file mode 100644 (file)
index 0000000..313e3a0
--- /dev/null
@@ -0,0 +1,24 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QCoreApplication
+
+from manager import OSMManager, CustomTextureData  # noqa: F401
+
+
+if __name__ == "__main__":
+    app = QGuiApplication(sys.argv)
+    engine = QQmlApplicationEngine()
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("OSMBuildings", "Main")
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    exit_code = QCoreApplication.exec()
+    del engine
+    sys.exit(exit_code)
diff --git a/examples/demos/osmbuildings/manager.py b/examples/demos/osmbuildings/manager.py
new file mode 100644 (file)
index 0000000..6ff2d20
--- /dev/null
@@ -0,0 +1,104 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtQuick3D import QQuick3DTextureData
+from PySide6.QtQml import QmlElement
+from PySide6.QtGui import QImage, QVector3D
+from PySide6.QtCore import QByteArray, QObject, Property, Slot, Signal
+
+from request import OSMTileData, OSMRequest
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "OSMBuildings"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class OSMManager(QObject):
+
+    mapsDataReady = Signal(QByteArray, int, int, int)
+
+    def __init__(self, parent=None):
+        super().__init__(parent)
+        self.m_request = OSMRequest(self)
+        self.m_startBuildingTileX = 17605
+        self.m_startBuildingTileY = 10746
+        self.m_tileSizeX = 37
+        self.m_tileSizeY = 37
+        self.m_request.mapsDataReady.connect(self._slotMapsDataReady)
+
+    def tileSizeX(self):
+        return self.m_tileSizeX
+
+    def tileSizeY(self):
+        return self.m_tileSizeY
+
+    @Slot(QByteArray, int, int, int)
+    def _slotMapsDataReady(self, mapData, tileX, tileY, zoomLevel):
+        self.mapsDataReady.emit(mapData, tileX - self.m_startBuildingTileX,
+                                tileY - self.m_startBuildingTileY, zoomLevel)
+
+    @Slot(QVector3D, QVector3D, float, float, float, float, float, float)
+    def setCameraProperties(self, position, right,
+                            cameraZoom, minimumZoom, maximumZoom,
+                            cameraTilt, minimumTilt, maximumTilt):
+
+        tiltFactor = (cameraTilt - minimumTilt) / max(maximumTilt - minimumTilt, 1.0)
+        zoomFactor = (cameraZoom - minimumZoom) / max(maximumZoom - minimumZoom, 1.0)
+
+        # Forward vector align to the XY plane
+        forwardVector = QVector3D.crossProduct(right, QVector3D(0.0, 0.0, -1.0)).normalized()
+        projectionOfForwardOnXY = position + forwardVector * tiltFactor * zoomFactor * 50.0
+
+        queue = []
+        for forwardIndex in range(-20, 21):
+            for sidewardIndex in range(-20, 21):
+                vx = float(self.m_tileSizeX * sidewardIndex)
+                vy = float(self.m_tileSizeY * forwardIndex)
+                transferredPosition = projectionOfForwardOnXY + QVector3D(vx, vy, 0)
+                tile_x = self.m_startBuildingTileX + int(transferredPosition.x() / self.m_tileSizeX)
+                tile_y = self.m_startBuildingTileY - int(transferredPosition.y() / self.m_tileSizeY)
+                self.addBuildingRequestToQueue(queue, tile_x, tile_y)
+
+        projectedTileX = (self.m_startBuildingTileX + int(projectionOfForwardOnXY.x()
+                          / self.m_tileSizeX))
+        projectedTileY = (self.m_startBuildingTileY - int(projectionOfForwardOnXY.y()
+                          / self.m_tileSizeY))
+
+        def tile_sort_key(tile_data):
+            return tile_data.distanceTo(projectedTileX, projectedTileY)
+
+        queue.sort(key=tile_sort_key)
+
+        self.m_request.getMapsData(queue.copy())
+
+    def addBuildingRequestToQueue(self, queue, tileX, tileY, zoomLevel=15):
+        queue.append(OSMTileData(tileX, tileY, zoomLevel))
+
+    @Slot(result=bool)
+    def isDemoToken(self):
+        return self.m_request.isDemoToken()
+
+    @Slot(str)
+    def setToken(self, token):
+        self.m_request.setToken(token)
+
+    @Slot(result=str)
+    def token(self):
+        return self.m_request.token()
+
+    tileSizeX = Property(int, tileSizeX, constant=True)
+    tileSizeY = Property(int, tileSizeY, constant=True)
+
+
+@QmlElement
+class CustomTextureData(QQuick3DTextureData):
+
+    @Slot(QByteArray)
+    def setImageData(self, data):
+        image = QImage.fromData(data).convertToFormat(QImage.Format.Format_RGBA8888)
+        self.setTextureData(QByteArray(bytearray(image.constBits())))
+        self.setSize(image.size())
+        self.setHasTransparency(False)
+        self.setFormat(QQuick3DTextureData.Format.RGBA8)
diff --git a/examples/demos/osmbuildings/osmbuildings.pyproject b/examples/demos/osmbuildings/osmbuildings.pyproject
new file mode 100644 (file)
index 0000000..2d02352
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "files": ["main.py", "manager.py", "request.py",
+              "OSMBuildings/Main.qml", "OSMBuildings/OSMCameraController.qml",
+              "OSMBuildings/customshadertiles.frag"]
+}
diff --git a/examples/demos/osmbuildings/request.py b/examples/demos/osmbuildings/request.py
new file mode 100644 (file)
index 0000000..a845bc2
--- /dev/null
@@ -0,0 +1,128 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import math
+import sys
+from dataclasses import dataclass
+from functools import partial
+
+from PySide6.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
+from PySide6.QtCore import (QByteArray, QTimer, QFile, QFileInfo,
+                            QObject, QUrl, Signal, Slot)
+
+# %1 = zoom level(is dynamic), %2 = x tile number, %3 = y tile number
+URL_OSMB_MAP = "https://tile-a.openstreetmap.fr/hot/{}/{}/{}.png"
+
+
+@dataclass
+class OSMTileData:
+    TileX: int = 0
+    TileY: int = 0
+    ZoomLevel: int = 1
+
+    def distanceTo(self, x, y):
+        deltaX = float(self.TileX) - float(x)
+        deltaY = float(self.TileY) - float(y)
+        return math.sqrt(deltaX * deltaX + deltaY * deltaY)
+
+    def __eq__(self, rhs):
+        return self._equals(rhs)
+
+    def __ne__(self, rhs):
+        return not self._equals(rhs)
+
+    def __hash__(self):
+        return hash((self.TileX, self.TileY, self.ZoomLevel))
+
+    def _equals(self, rhs):
+        return (self.TileX == rhs.TileX and self.TileY == rhs.TileY
+                and self.ZoomLevel == rhs.ZoomLevel)
+
+
+def tileKey(tile):
+    return f"{tile.ZoomLevel},{tile.TileX},{tile.TileY}"
+
+
+class OSMRequest(QObject):
+
+    mapsDataReady = Signal(QByteArray, int, int, int)
+
+    def __init__(self, parent):
+        super().__init__(parent)
+
+        self.m_mapsNumberOfRequestsInFlight = 0
+        self.m_queuesTimer = QTimer()
+        self.m_queuesTimer.setInterval(0)
+        self.m_buildingsQueue = []
+        self.m_mapsQueue = []
+        self.m_networkAccessManager = QNetworkAccessManager()
+        self.m_token = ""
+
+        self.m_queuesTimer.timeout.connect(self._slotTimeOut)
+        self.m_queuesTimer.setInterval(0)
+        self.m_lastBuildingsDataError = ""
+        self.m_lastMapsDataError = ""
+
+    @Slot()
+    def _slotTimeOut(self):
+        if not self.m_buildingsQueue and not self.m_mapsQueue:
+            self.m_queuesTimer.stop()
+        else:
+            numConcurrentRequests = 6
+            if self.m_mapsQueue and self.m_mapsNumberOfRequestsInFlight < numConcurrentRequests:
+                self.getMapsDataRequest(self.m_mapsQueue[0])
+                del self.m_mapsQueue[0]
+
+                self.m_mapsNumberOfRequestsInFlight += 1
+
+    def isDemoToken(self):
+        return not self.m_token
+
+    def token(self):
+        return self.m_token
+
+    def setToken(self, token):
+        self.m_token = token
+
+    def getBuildingsData(self, buildingsQueue):
+        if not buildingsQueue:
+            return
+        self.m_buildingsQueue = buildingsQueue
+        if not self.m_queuesTimer.isActive():
+            self.m_queuesTimer.start()
+
+    def getMapsData(self, mapsQueue):
+        if not mapsQueue:
+            return
+        self.m_mapsQueue = mapsQueue
+        if not self.m_queuesTimer.isActive():
+            self.m_queuesTimer.start()
+
+    def getMapsDataRequest(self, tile):
+        fileName = "data/" + tileKey(tile) + ".png"
+        if QFileInfo.exists(fileName):
+            file = QFile(fileName)
+            if file.open(QFile.OpenModeFlag.ReadOnly):
+                data = file.readAll()
+                file.close()
+                self.mapsDataReady.emit(data, tile.TileX, tile.TileY, tile.ZoomLevel)
+                self.m_mapsNumberOfRequestsInFlight -= 1
+                return
+
+        url = QUrl(URL_OSMB_MAP.format(tile.ZoomLevel, tile.TileX, tile.TileY))
+        reply = self.m_networkAccessManager.get(QNetworkRequest(url))
+        reply.finished.connect(partial(self._mapsDataReceived, reply, tile))
+
+    @Slot(OSMTileData)
+    def _mapsDataReceived(self, reply, tile):
+        reply.deleteLater()
+        if reply.error() == QNetworkReply.NetworkError.NoError:
+            data = reply.readAll()
+            self.mapsDataReady.emit(data, tile.TileX, tile.TileY, tile.ZoomLevel)
+        else:
+            message = reply.readAll().data().decode('utf-8')
+            if message != self.m_lastMapsDataError:
+                self.m_lastMapsDataError = message
+                print("OSMRequest.getMapsDataRequest", reply.error(),
+                      reply.url(), message, file=sys.stderr)
+        self.m_mapsNumberOfRequestsInFlight -= 1
index dd7fe1679ff8d787d8bfc73583a95294ce551738..cbd893afcec345bd9f8c0e5c3b386f7b300a647f 100644 (file)
@@ -1,5 +1,3 @@
-.. _task-menu-extension-example:
-
 Task Menu Extension Example
 ===========================
 
index ffde89d296be9b103e4269346fa39d5143c7b353..ff2cda53e6fb77bc292e46a23b956419b34da806 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt Designer taskmenuextension example from Qt v6.x"""
 
index 2b014c790cad167c85ae940f80757fd1824a2a9a..853355587e75364aca57b5f3b64541c784bae7b3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from tictactoe import TicTacToe  # noqa: F401
 from tictactoeplugin import TicTacToePlugin
index aa1c3158c8cfe020b12a135b8be805b5906ea8b7..f334563a78d87e7de18174bb7fcdc26e0ce38d68 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, QPoint, QRect, QSize, Property, Slot
 from PySide6.QtGui import QPainter, QPen
@@ -63,9 +64,9 @@ class TicTacToe(QWidget):
 
     def paintEvent(self, event):
         with QPainter(self) as painter:
-            painter.setRenderHint(QPainter.Antialiasing)
+            painter.setRenderHint(QPainter.RenderHint.Antialiasing)
 
-            painter.setPen(QPen(Qt.darkGreen, 1))
+            painter.setPen(QPen(Qt.GlobalColor.darkGreen, 1))
             painter.drawLine(self._cell_width(), 0,
                              self._cell_width(), self.height())
             painter.drawLine(2 * self._cell_width(), 0,
@@ -75,7 +76,7 @@ class TicTacToe(QWidget):
             painter.drawLine(0, 2 * self._cell_height(),
                              self.width(), 2 * self._cell_height())
 
-            painter.setPen(QPen(Qt.darkBlue, 2))
+            painter.setPen(QPen(Qt.GlobalColor.darkBlue, 2))
 
             for position in range(9):
                 cell = self._cell_rect(position)
@@ -85,7 +86,7 @@ class TicTacToe(QWidget):
                 elif self._state[position] == NOUGHT:
                     painter.drawEllipse(cell)
 
-            painter.setPen(QPen(Qt.yellow, 3))
+            painter.setPen(QPen(Qt.GlobalColor.yellow, 3))
 
             for position in range(0, 8, 3):
                 if (self._state[position] != EMPTY
index f9c925133a62dd3c4505020ac8d1c562c24c2ba8..93a40a85a30c9a828cd9ab44b89f125df9f27f1b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from tictactoe import TicTacToe
 from tictactoetaskmenu import TicTacToeTaskMenuFactory
index 71826dc7ddcbbd21dc3e512d9e58edeea2031455..a169a4f576d61360d71e9a6b53240d4380377e0b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from tictactoe import TicTacToe
 
@@ -15,12 +16,12 @@ class TicTacToeDialog(QDialog):
         layout = QVBoxLayout(self)
         self._ticTacToe = TicTacToe(self)
         layout.addWidget(self._ticTacToe)
-        button_box = QDialogButtonBox(QDialogButtonBox.Ok
-                                      | QDialogButtonBox.Cancel
-                                      | QDialogButtonBox.Reset)
+        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+                                      | QDialogButtonBox.StandardButton.Cancel
+                                      | QDialogButtonBox.StandardButton.Reset)
         button_box.accepted.connect(self.accept)
         button_box.rejected.connect(self.reject)
-        reset_button = button_box.button(QDialogButtonBox.Reset)
+        reset_button = button_box.button(QDialogButtonBox.StandardButton.Reset)
         reset_button.clicked.connect(self._ticTacToe.clear_board)
         layout.addWidget(button_box)
 
@@ -48,7 +49,7 @@ class TicTacToeTaskMenu(QPyDesignerTaskMenuExtension):
     def _edit_state(self):
         dialog = TicTacToeDialog(self._ticTacToe)
         dialog.set_state(self._ticTacToe.state)
-        if dialog.exec() == QDialog.Accepted:
+        if dialog.exec() == QDialog.DialogCode.Accepted:
             self._ticTacToe.state = dialog.state()
 
 
index 8bfcc4ca22300d4ccedfcbb85e078ffc66951aa4..0da07b40344939924284490c885eb407dcabb344 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -36,7 +37,8 @@ class ApplicationWindow(QMainWindow):
         self.menu_file.addAction(exit)
 
         self.menu_about = self.menu.addMenu("&About")
-        about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
+        about = QAction("About Qt", self,
+                        shortcut=QKeySequence(QKeySequence.StandardKey.HelpContents),
                         triggered=qApp.aboutQt)  # noqa: F821
         self.menu_about.addAction(about)
 
@@ -47,8 +49,8 @@ class ApplicationWindow(QMainWindow):
         # Sliders (Left)
         min = 0
         max = 360
-        self.slider_azim = QSlider(minimum=min, maximum=max, orientation=Qt.Horizontal)
-        self.slider_elev = QSlider(minimum=min, maximum=max, orientation=Qt.Horizontal)
+        self.slider_azim = QSlider(minimum=min, maximum=max, orientation=Qt.Orientation.Horizontal)
+        self.slider_elev = QSlider(minimum=min, maximum=max, orientation=Qt.Orientation.Horizontal)
 
         self.slider_azim_layout = QHBoxLayout()
         self.slider_azim_layout.addWidget(QLabel(f"{min}"))
@@ -63,7 +65,7 @@ class ApplicationWindow(QMainWindow):
         # Table (Right)
         self.table = QTableWidget()
         header = self.table.horizontalHeader()
-        header.setSectionResizeMode(QHeaderView.Stretch)
+        header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
 
         # ComboBox (Right)
         self.combo = QComboBox()
index 2423e496a659a56da935ee1d36a7d673119a1050..22669d1a5eaeaa617f83ec93879d362fc8c983c8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 8cd7e7903da6af56a297acf449ee0e1bcd548020..696a3f4f2d369e52461306a789ff36584dbe97b6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 import math
@@ -33,9 +34,9 @@ class Node(QGraphicsObject):
         self._radius = 30
         self._rect = QRectF(0, 0, self._radius * 2, self._radius * 2)
 
-        self.setFlag(QGraphicsItem.ItemIsMovable)
-        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
-        self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges)
+        self.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
 
     def boundingRect(self) -> QRectF:
         """Override from QGraphicsItem
@@ -54,20 +55,20 @@ class Node(QGraphicsObject):
             painter (QPainter)
             option (QStyleOptionGraphicsItem)
         """
-        painter.setRenderHints(QPainter.Antialiasing)
+        painter.setRenderHints(QPainter.RenderHint.Antialiasing)
         painter.setPen(
             QPen(
                 QColor(self._color).darker(),
                 2,
-                Qt.SolidLine,
-                Qt.RoundCap,
-                Qt.RoundJoin,
+                Qt.PenStyle.SolidLine,
+                Qt.PenCapStyle.RoundCap,
+                Qt.PenJoinStyle.RoundJoin,
             )
         )
         painter.setBrush(QBrush(QColor(self._color)))
         painter.drawEllipse(self.boundingRect())
         painter.setPen(QPen(QColor("white")))
-        painter.drawText(self.boundingRect(), Qt.AlignCenter, self._name)
+        painter.drawText(self.boundingRect(), Qt.AlignmentFlag.AlignCenter, self._name)
 
     def add_edge(self, edge):
         """Add an edge to this node
@@ -87,7 +88,7 @@ class Node(QGraphicsObject):
         Returns:
             Any
         """
-        if change == QGraphicsItem.ItemPositionHasChanged:
+        if change == QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged:
             for edge in self._edges:
                 edge.adjust()
 
@@ -204,15 +205,15 @@ class Edge(QGraphicsItem):
         """
 
         if self._source and self._dest:
-            painter.setRenderHints(QPainter.Antialiasing)
+            painter.setRenderHints(QPainter.RenderHint.Antialiasing)
 
             painter.setPen(
                 QPen(
                     QColor(self._color),
                     self._tickness,
-                    Qt.SolidLine,
-                    Qt.RoundCap,
-                    Qt.RoundJoin,
+                    Qt.PenStyle.SolidLine,
+                    Qt.PenCapStyle.RoundCap,
+                    Qt.PenJoinStyle.RoundJoin,
                 )
             )
             painter.drawLine(self._line)
@@ -285,7 +286,7 @@ class GraphView(QGraphicsView):
                 animation = QPropertyAnimation(item, b"pos")
                 animation.setDuration(1000)
                 animation.setEndValue(QPointF(x, y))
-                animation.setEasingCurve(QEasingCurve.OutExpo)
+                animation.setEasingCurve(QEasingCurve.Type.OutExpo)
                 self.animations.addAnimation(animation)
 
             self.animations.start()
index 0c55a13336ce92e4aefad8c8a415a13e24aff9f5..dfc59397a68c0f30e3a3d2cf24b77b81c2685a4b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 import sys
@@ -56,8 +57,8 @@ class Thread(QThread):
 
             # Creating and scaling QImage
             h, w, ch = color_frame.shape
-            img = QImage(color_frame.data, w, h, ch * w, QImage.Format_RGB888)
-            scaled_img = img.scaled(640, 480, Qt.KeepAspectRatio)
+            img = QImage(color_frame.data, w, h, ch * w, QImage.Format.Format_RGB888)
+            scaled_img = img.scaled(640, 480, Qt.AspectRatioMode.KeepAspectRatio)
 
             # Emit signal
             self.updateFrame.emit(scaled_img)
@@ -78,7 +79,8 @@ class Window(QMainWindow):
         self.menu_file.addAction(exit)
 
         self.menu_about = self.menu.addMenu("&About")
-        about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
+        about = QAction("About Qt", self,
+                        shortcut=QKeySequence(QKeySequence.StandardKey.HelpContents),
                         triggered=qApp.aboutQt)  # noqa: F821
         self.menu_about.addAction(about)
 
@@ -93,7 +95,7 @@ class Window(QMainWindow):
 
         # Model group
         self.group_model = QGroupBox("Trained model")
-        self.group_model.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
+        self.group_model.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
         model_layout = QHBoxLayout()
 
         self.combobox = QComboBox()
@@ -109,8 +111,8 @@ class Window(QMainWindow):
         buttons_layout = QHBoxLayout()
         self.button1 = QPushButton("Start")
         self.button2 = QPushButton("Stop/Close")
-        self.button1.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
-        self.button2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
+        self.button1.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
+        self.button2.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
         buttons_layout.addWidget(self.button2)
         buttons_layout.addWidget(self.button1)
 
index b3d9e81fe9ba1bf46582b69d55842ecc3e43bfea..bd741f9772b43126fe8e9a46b87e4e33a69cab93 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import pandas as pd
 
@@ -42,7 +43,7 @@ class PandasModel(QAbstractTableModel):
         if not index.isValid():
             return None
 
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             return str(self._dataframe.iloc[index.row(), index.column()])
 
         return None
@@ -54,8 +55,8 @@ class PandasModel(QAbstractTableModel):
 
         Return dataframe index as vertical header data and columns as horizontal header data.
         """
-        if role == Qt.DisplayRole:
-            if orientation == Qt.Horizontal:
+        if role == Qt.ItemDataRole.DisplayRole:
+            if orientation == Qt.Orientation.Horizontal:
                 return str(self._dataframe.columns[section])
 
             if orientation == Qt.Vertical:
index 94fdc3bdc91b7c48826a68b64e8d5d3e6fe3b8e7..ce886a8e8af2bf2e4c4aa9461d9e803b111b6608 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index b1844aec4c0f7c8b663b81bf275d973e83d66e9b..815e365ee4bf24d07c8bde4c9dfc2ea078fc5c41 100644 (file)
@@ -30,23 +30,24 @@ Item {
             GraphsView {
                 anchors.fill: parent
                 anchors.margins: 16
-                theme: GraphTheme {
-                    colorTheme: GraphTheme.ColorThemeDark
+                axisX: BarCategoryAxis {
+                    categories: [2024, 2025, 2026]
+                    gridVisible: false
+                    subGridVisible: false
+                }
+                axisY: ValueAxis {
+                    min: 20
+                    max: 100
+                    tickInterval: 10
+                    subTickCount: 9
+                }
+                theme: GraphsTheme {
+                    colorScheme: Qt.Dark
+                    theme: GraphsTheme.Theme.QtGreen
                 }
                 //! [bargraph]
                 //! [barseries]
                 BarSeries {
-                    axisX: BarCategoryAxis {
-                        categories: [2024, 2025, 2026]
-                        gridVisible: false
-                        minorGridVisible: false
-                    }
-                    axisY: ValueAxis {
-                        min: 20
-                        max: 100
-                        tickInterval: 10
-                        minorTickCount: 9
-                    }
                     //! [barseries]
                     //! [barset]
                     BarSet {
@@ -72,19 +73,32 @@ Item {
             GraphsView {
                 anchors.fill: parent
                 anchors.margins: 16
-                theme: GraphTheme {
+                theme: GraphsTheme {
                     readonly property color c1: "#DBEB00"
                     readonly property color c2: "#373F26"
                     readonly property color c3: Qt.lighter(c2, 1.5)
-                    colorTheme: GraphTheme.ColorThemeDark
-                    gridMajorBarsColor: c3
-                    gridMinorBarsColor: c2
-                    axisXMajorColor: c3
-                    axisYMajorColor: c3
-                    axisXMinorColor: c2
-                    axisYMinorColor: c2
-                    axisXLabelsColor: c1
-                    axisYLabelsColor: c1
+                    colorScheme: Qt.Dark
+                    seriesColors: ["#2CDE85", "#DBEB00"]
+                    grid.mainColor: c3
+                    grid.subColor: c2
+                    axisX.mainColor: c3
+                    axisY.mainColor: c3
+                    axisX.subColor: c2
+                    axisY.subColor: c2
+                    axisX.labelTextColor: c1
+                    axisY.labelTextColor: c1
+                }
+                axisX: ValueAxis {
+                    max: 5
+                    tickInterval: 1
+                    subTickCount: 9
+                    labelDecimals: 1
+                }
+                axisY: ValueAxis {
+                    max: 10
+                    tickInterval: 1
+                    subTickCount: 4
+                    labelDecimals: 1
                 }
                 //! [linegraph]
 
@@ -99,31 +113,11 @@ Item {
                 }
                 //! [linemarker]
 
-                //! [lineseriestheme]
-                SeriesTheme {
-                    id: seriesTheme
-                    colors: ["#2CDE85", "#DBEB00"]
-                }
-                //! [lineseriestheme]
-
                 //! [lineseries1]
                 LineSeries {
                     id: lineSeries1
-                    theme: seriesTheme
-                    axisX: ValueAxis {
-                        max: 5
-                        tickInterval: 1
-                        minorTickCount: 9
-                        labelDecimals: 1
-                    }
-                    axisY: ValueAxis {
-                        max: 10
-                        tickInterval: 1
-                        minorTickCount: 4
-                        labelDecimals: 1
-                    }
                     width: 4
-                    pointMarker: Marker { }
+                    pointDelegate: Marker { }
                     XYPoint { x: 0; y: 0 }
                     XYPoint { x: 1; y: 2.1 }
                     XYPoint { x: 2; y: 3.3 }
@@ -136,9 +130,8 @@ Item {
                 //! [lineseries2]
                 LineSeries {
                     id: lineSeries2
-                    theme: seriesTheme
                     width: 4
-                    pointMarker: Marker { }
+                    pointDelegate: Marker { }
                     XYPoint { x: 0; y: 5.0 }
                     XYPoint { x: 1; y: 3.3 }
                     XYPoint { x: 2; y: 7.1 }
index acc349beb10db899a990b588b246a8264bd0f5d6..aff9d129a4a9852f6d9ddf4a5525cd0476a410c9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt Hello Graphs example from Qt v6.x"""
 
diff --git a/examples/graphs/3d/bars/Bars/Axes.qml b/examples/graphs/3d/bars/Bars/Axes.qml
new file mode 100644 (file)
index 0000000..db87a74
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+
+Item {
+    property alias column: columnAxis
+    property alias row: rowAxis
+    property alias value: valueAxis
+    property alias total: totalAxis
+
+    // Custom labels for columns, since the data contains abbreviated month names.
+    //! [0]
+    Category3DAxis {
+        id: columnAxis
+        labels: ["January", "February", "March", "April", "May", "June",
+            "July", "August", "September", "October", "November", "December"]
+        labelAutoAngle: 30
+    }
+    //! [0]
+    Category3DAxis {
+        id: totalAxis
+        labels: ["Yearly total"]
+        labelAutoAngle: 30
+    }
+    Category3DAxis {
+        // For row labels we can use row labels from data proxy, no labels defined for rows.
+        id: rowAxis
+        labelAutoAngle: 30
+    }
+
+    Value3DAxis {
+        id: valueAxis
+        min: 0
+        max: 35
+        labelFormat: "%.2f M\u20AC"
+        title: "Monthly income"
+        labelAutoAngle: 90
+    }
+}
diff --git a/examples/graphs/3d/bars/Bars/Data.qml b/examples/graphs/3d/bars/Bars/Data.qml
new file mode 100644 (file)
index 0000000..b088d4b
--- /dev/null
@@ -0,0 +1,118 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQml.Models
+
+Item {
+    property alias model: dataModel
+
+    property var modelAsJsArray: {
+        var arr = [];
+        for (var i = 0; i < dataModel.count; i++) {
+            var row = dataModel.get(i);
+            arr.push({
+                         timestamp: row.timestamp,
+                         expenses: row.expenses,
+                         income: row.income
+                     });
+        }
+        return arr;
+    }
+
+    //! [0]
+    ListModel {
+        id: dataModel
+        ListElement{ timestamp: "2016-01"; expenses: "-4";  income: "5" }
+        ListElement{ timestamp: "2016-02"; expenses: "-5";  income: "6" }
+        ListElement{ timestamp: "2016-03"; expenses: "-7";  income: "4" }
+        //! [0]
+        ListElement{ timestamp: "2016-04"; expenses: "-3";  income: "2" }
+        ListElement{ timestamp: "2016-05"; expenses: "-4";  income: "1" }
+        ListElement{ timestamp: "2016-06"; expenses: "-2";  income: "2" }
+        ListElement{ timestamp: "2016-07"; expenses: "-1";  income: "3" }
+        ListElement{ timestamp: "2016-08"; expenses: "-5";  income: "1" }
+        ListElement{ timestamp: "2016-09"; expenses: "-2";  income: "3" }
+        ListElement{ timestamp: "2016-10"; expenses: "-5";  income: "2" }
+        ListElement{ timestamp: "2016-11"; expenses: "-8";  income: "5" }
+        ListElement{ timestamp: "2016-12"; expenses: "-3";  income: "3" }
+
+        ListElement{ timestamp: "2017-01"; expenses: "-3";  income: "1" }
+        ListElement{ timestamp: "2017-02"; expenses: "-4";  income: "2" }
+        ListElement{ timestamp: "2017-03"; expenses: "-12"; income: "4" }
+        ListElement{ timestamp: "2017-04"; expenses: "-13"; income: "6" }
+        ListElement{ timestamp: "2017-05"; expenses: "-14"; income: "11" }
+        ListElement{ timestamp: "2017-06"; expenses: "-7";  income: "7" }
+        ListElement{ timestamp: "2017-07"; expenses: "-6";  income: "4" }
+        ListElement{ timestamp: "2017-08"; expenses: "-4";  income: "15" }
+        ListElement{ timestamp: "2017-09"; expenses: "-2";  income: "18" }
+        ListElement{ timestamp: "2017-10"; expenses: "-29"; income: "25" }
+        ListElement{ timestamp: "2017-11"; expenses: "-23"; income: "29" }
+        ListElement{ timestamp: "2017-12"; expenses: "-5";  income: "9" }
+
+        ListElement{ timestamp: "2018-01"; expenses: "-3";  income: "8" }
+        ListElement{ timestamp: "2018-02"; expenses: "-8";  income: "14" }
+        ListElement{ timestamp: "2018-03"; expenses: "-10"; income: "20" }
+        ListElement{ timestamp: "2018-04"; expenses: "-12"; income: "24" }
+        ListElement{ timestamp: "2018-05"; expenses: "-10"; income: "19" }
+        ListElement{ timestamp: "2018-06"; expenses: "-5";  income: "8" }
+        ListElement{ timestamp: "2018-07"; expenses: "-1";  income: "4" }
+        ListElement{ timestamp: "2018-08"; expenses: "-7";  income: "12" }
+        ListElement{ timestamp: "2018-09"; expenses: "-4";  income: "16" }
+        ListElement{ timestamp: "2018-10"; expenses: "-22"; income: "33" }
+        ListElement{ timestamp: "2018-11"; expenses: "-16"; income: "25" }
+        ListElement{ timestamp: "2018-12"; expenses: "-2";  income: "7" }
+
+        ListElement{ timestamp: "2019-01"; expenses: "-4";  income: "5"  }
+        ListElement{ timestamp: "2019-02"; expenses: "-4";  income: "7"  }
+        ListElement{ timestamp: "2019-03"; expenses: "-11"; income: "14"  }
+        ListElement{ timestamp: "2019-04"; expenses: "-16"; income: "22"  }
+        ListElement{ timestamp: "2019-05"; expenses: "-3";  income: "5"  }
+        ListElement{ timestamp: "2019-06"; expenses: "-4";  income: "8"  }
+        ListElement{ timestamp: "2019-07"; expenses: "-7";  income: "9"  }
+        ListElement{ timestamp: "2019-08"; expenses: "-9";  income: "13"  }
+        ListElement{ timestamp: "2019-09"; expenses: "-1";  income: "6"  }
+        ListElement{ timestamp: "2019-10"; expenses: "-14"; income: "25"  }
+        ListElement{ timestamp: "2019-11"; expenses: "-19"; income: "29"  }
+        ListElement{ timestamp: "2019-12"; expenses: "-5";  income: "7"  }
+
+        ListElement{ timestamp: "2020-01"; expenses: "-14"; income: "22"  }
+        ListElement{ timestamp: "2020-02"; expenses: "-5";  income: "7"  }
+        ListElement{ timestamp: "2020-03"; expenses: "-1";  income: "9"  }
+        ListElement{ timestamp: "2020-04"; expenses: "-1";  income: "12"  }
+        ListElement{ timestamp: "2020-05"; expenses: "-5";  income: "9"  }
+        ListElement{ timestamp: "2020-06"; expenses: "-5";  income: "8"  }
+        ListElement{ timestamp: "2020-07"; expenses: "-3";  income: "7"  }
+        ListElement{ timestamp: "2020-08"; expenses: "-1";  income: "5"  }
+        ListElement{ timestamp: "2020-09"; expenses: "-2";  income: "4"  }
+        ListElement{ timestamp: "2020-10"; expenses: "-10"; income: "13"  }
+        ListElement{ timestamp: "2020-11"; expenses: "-12"; income: "17"  }
+        ListElement{ timestamp: "2020-12"; expenses: "-6";  income: "9"  }
+
+        ListElement{ timestamp: "2021-01"; expenses: "-2";  income: "6"  }
+        ListElement{ timestamp: "2021-02"; expenses: "-4";  income: "8"  }
+        ListElement{ timestamp: "2021-03"; expenses: "-7";  income: "12"  }
+        ListElement{ timestamp: "2021-04"; expenses: "-9";  income: "15"  }
+        ListElement{ timestamp: "2021-05"; expenses: "-7";  income: "19"  }
+        ListElement{ timestamp: "2021-06"; expenses: "-9";  income: "18"  }
+        ListElement{ timestamp: "2021-07"; expenses: "-13"; income: "17"  }
+        ListElement{ timestamp: "2021-08"; expenses: "-5";  income: "9"  }
+        ListElement{ timestamp: "2021-09"; expenses: "-3";  income: "8"  }
+        ListElement{ timestamp: "2021-10"; expenses: "-13"; income: "15"  }
+        ListElement{ timestamp: "2021-11"; expenses: "-8";  income: "17"  }
+        ListElement{ timestamp: "2021-12"; expenses: "-7";  income: "10"  }
+
+        ListElement{ timestamp: "2022-01"; expenses: "-12";  income: "16"  }
+        ListElement{ timestamp: "2022-02"; expenses: "-24";  income: "28"  }
+        ListElement{ timestamp: "2022-03"; expenses: "-27";  income: "22"  }
+        ListElement{ timestamp: "2022-04"; expenses: "-29";  income: "25"  }
+        ListElement{ timestamp: "2022-05"; expenses: "-27";  income: "29"  }
+        ListElement{ timestamp: "2022-06"; expenses: "-19";  income: "18"  }
+        ListElement{ timestamp: "2022-07"; expenses: "-13";  income: "17"  }
+        ListElement{ timestamp: "2022-08"; expenses: "-15";  income: "19"  }
+        ListElement{ timestamp: "2022-09"; expenses: "-3";   income: "8"  }
+        ListElement{ timestamp: "2022-10"; expenses: "-3";   income: "6"  }
+        ListElement{ timestamp: "2022-11"; expenses: "-4";   income: "8"  }
+        ListElement{ timestamp: "2022-12"; expenses: "-5";   income: "9"  }
+    }
+}
diff --git a/examples/graphs/3d/bars/Bars/Main.qml b/examples/graphs/3d/bars/Bars/Main.qml
new file mode 100644 (file)
index 0000000..e839fc3
--- /dev/null
@@ -0,0 +1,484 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import QtGraphs
+import Qt.labs.qmlmodels
+
+pragma ComponentBehavior: Bound
+
+Item {
+    id: mainview
+    width: 1280
+    height: 1024
+
+    property int buttonLayoutHeight: 180
+    property int currentRow
+    state: Screen.width < Screen.height ? "portrait" : "landscape"
+
+    Data {
+        id: graphData
+    }
+
+    Axes {
+        id: graphAxes
+    }
+
+    property Bar3DSeries selectedSeries
+    selectedSeries: barSeries
+
+    function handleSelectionChange(series, position) {
+        if (position !== series.invalidSelectionPosition)
+            selectedSeries = series
+
+        // Set tableView current row to selected bar
+        var rowRole = series.rowLabels[position.x]
+        var colRole
+        if (barGraph.columnAxis == graphAxes.total)
+            colRole = "01"
+        else
+            colRole = series.columnLabels[position.y]
+        var checkTimestamp = rowRole + "-" + colRole
+
+        if (currentRow === -1 || checkTimestamp !== graphData.model.get(currentRow).timestamp) {
+            var totalRows = tableView.rows
+            for (var i = 0; i < totalRows; i++) {
+                var modelTimestamp = graphData.model.get(i).timestamp
+                if (modelTimestamp === checkTimestamp) {
+                    currentRow = i
+                    break
+                }
+            }
+        }
+    }
+
+    ColumnLayout {
+        id: tableViewLayout
+
+        anchors.top: parent.top
+        anchors.left: parent.left
+
+        HorizontalHeaderView {
+            id: headerView
+            readonly property var columnNames: ["Month", "Expenses", "Income"]
+
+            syncView: tableView
+            Layout.fillWidth: true
+            delegate: Text {
+                required property int index
+                padding: 3
+                text: headerView.columnNames[index]
+                color: barGraph.theme.labelTextColor
+                horizontalAlignment: Text.AlignHCenter
+                verticalAlignment: Text.AlignVCenter
+                elide: Text.ElideRight
+            }
+        }
+
+        TableView {
+            id: tableView
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            reuseItems: false
+            clip: true
+
+            model: TableModel {
+                id: tableModel
+                TableModelColumn { display: "timestamp" }
+                TableModelColumn { display: "expenses" }
+                TableModelColumn { display: "income" }
+
+                rows: graphData.modelAsJsArray
+            }
+
+            delegate: Rectangle {
+                id: delegateRoot
+                required property int row
+                required property int column
+                required property string display
+                implicitHeight: 30
+                implicitWidth: column === 0 ? tableView.width / 2 : tableView.width / 4
+                color: row === mainview.currentRow ? barGraph.theme.grid.mainColor
+                                                   : barGraph.theme.backgroundColor
+                border.color: row === mainview.currentRow ? barGraph.theme.labelTextColor
+                                                          : barGraph.theme.grid.mainColor
+                border.width: 1
+                MouseArea {
+                    anchors.fill: parent
+                    onClicked: {
+                        mainview.currentRow = delegateRoot.row
+
+                        //! [2]
+                        var timestamp = graphData.model.get(mainview.currentRow).timestamp
+                        var pattern = /(\d\d\d\d)-(\d\d)/
+                        var matches = pattern.exec(timestamp)
+                        var rowIndex = modelProxy.rowCategoryIndex(matches[1])
+                        var colIndex
+
+                        if (barGraph.columnAxis == graphAxes.total)
+                            colIndex = 0 // Just one column when showing yearly totals
+                        else
+                            colIndex = modelProxy.columnCategoryIndex(matches[2])
+
+                        if (selectedSeries.visible)
+                            mainview.selectedSeries.selectedBar = Qt.point(rowIndex, colIndex)
+                        else if (barSeries.visible)
+                            barSeries.selectedBar = Qt.point(rowIndex, colIndex)
+                        else
+                            secondarySeries.selectedBar = Qt.point(rowIndex, colIndex)
+                        //! [2]
+                    }
+                }
+
+                Text {
+                    id: delegateText
+                    anchors.verticalCenter: parent.verticalCenter
+                    width: parent.width
+                    anchors.leftMargin: 4
+                    anchors.left: parent.left
+                    anchors.right: parent.right
+                    text: formattedText
+                    property string formattedText: {
+                        if (delegateRoot.column === 0) {
+                            if (delegateRoot.display !== "") {
+                                var pattern = /(\d\d\d\d)-(\d\d)/
+                                var matches = pattern.exec(delegateRoot.display)
+                                var colIndex = parseInt(matches[2], 10) - 1
+                                return matches[1] + " - " + graphAxes.column.labels[colIndex]
+                            }
+                        } else {
+                            return delegateRoot.display
+                        }
+                    }
+                    color: delegateRoot.row === mainview.currentRow ? barGraph.theme.backgroundColor
+                                                                    : barGraph.theme.labelTextColor
+                    horizontalAlignment: delegateRoot.column === 0 ? Text.AlignLeft
+                                                                   : Text.AlignHCenter
+                    elide: Text.ElideRight
+                }
+            }
+        }
+    }
+
+    ColumnLayout {
+        id: controlLayout
+        spacing: 0
+
+        Button {
+            id: changeDataButton
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+            text: "Show 2020 - 2022"
+            clip: true
+            //! [1]
+            onClicked: {
+                if (text === "Show yearly totals") {
+                    modelProxy.autoRowCategories = true
+                    secondaryProxy.autoRowCategories = true
+                    modelProxy.columnRolePattern = /^.*$/
+                    secondaryProxy.columnRolePattern = /^.*$/
+                    graphAxes.value.autoAdjustRange = true
+                    barGraph.columnAxis = graphAxes.total
+                    text = "Show all years"
+                } else if (text === "Show all years") {
+                    modelProxy.autoRowCategories = true
+                    secondaryProxy.autoRowCategories = true
+                    modelProxy.columnRolePattern = /^.*-(\d\d)$/
+                    secondaryProxy.columnRolePattern = /^.*-(\d\d)$/
+                    graphAxes.value.min = 0
+                    graphAxes.value.max = 35
+                    barGraph.columnAxis = graphAxes.column
+                    text = "Show 2020 - 2022"
+                } else { // text === "Show 2020 - 2022"
+                    // Explicitly defining row categories, since we do not want to show data for
+                    // all years in the model, just for the selected ones.
+                    modelProxy.autoRowCategories = false
+                    secondaryProxy.autoRowCategories = false
+                    modelProxy.rowCategories = ["2020", "2021", "2022"]
+                    secondaryProxy.rowCategories = ["2020", "2021", "2022"]
+                    text = "Show yearly totals"
+                }
+            }
+            //! [1]
+
+            contentItem: Text {
+                text: changeDataButton.text
+                opacity: changeDataButton.enabled ? 1.0 : 0.3
+                color: barGraph.theme.labelTextColor
+                horizontalAlignment: Text.AlignHCenter
+                verticalAlignment: Text.AlignVCenter
+                elide: Text.ElideRight
+            }
+
+            background: Rectangle {
+                opacity: changeDataButton.enabled ? 1 : 0.3
+                color: changeDataButton.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+                border.color: changeDataButton.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+                border.width: 1
+                radius: 2
+            }
+        }
+
+        Button {
+            id: shadowToggle
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+            text: "Hide Shadows"
+            clip: true
+            onClicked: {
+                if (barGraph.shadowQuality == Graphs3D.ShadowQuality.None) {
+                    barGraph.shadowQuality = Graphs3D.ShadowQuality.SoftHigh
+                    text = "Hide Shadows"
+                } else {
+                    barGraph.shadowQuality = Graphs3D.ShadowQuality.None
+                    text = "Show Shadows"
+                }
+            }
+            contentItem: Text {
+                text: shadowToggle.text
+                opacity: shadowToggle.enabled ? 1.0 : 0.3
+                color: barGraph.theme.labelTextColor
+                horizontalAlignment: Text.AlignHCenter
+                verticalAlignment: Text.AlignVCenter
+                elide: Text.ElideRight
+            }
+
+            background: Rectangle {
+                opacity: shadowToggle.enabled ? 1 : 0.3
+                color: shadowToggle.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+                border.color: shadowToggle.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+                border.width: 1
+                radius: 2
+            }
+        }
+
+        Button {
+            id: seriesToggle
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+            text: "Show Expenses"
+            clip: true
+            //! [0]
+            onClicked: {
+                if (text === "Show Expenses") {
+                    barSeries.visible = false
+                    secondarySeries.visible = true
+                    barGraph.valueAxis.labelFormat = "-%.2f M\u20AC"
+                    secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: @valueLabel"
+                    text = "Show Both"
+                } else if (text === "Show Both") {
+                    barSeries.visible = true
+                    barGraph.valueAxis.labelFormat = "%.2f M\u20AC"
+                    secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: -@valueLabel"
+                    text = "Show Income"
+                } else { // text === "Show Income"
+                    secondarySeries.visible = false
+                    text = "Show Expenses"
+                }
+            }
+            //! [0]
+            contentItem: Text {
+                text: seriesToggle.text
+                opacity: seriesToggle.enabled ? 1.0 : 0.3
+                color: barGraph.theme.labelTextColor
+                horizontalAlignment: Text.AlignHCenter
+                verticalAlignment: Text.AlignVCenter
+                elide: Text.ElideRight
+            }
+
+            background: Rectangle {
+                opacity: seriesToggle.enabled ? 1 : 0.3
+                color: seriesToggle.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+                border.color: seriesToggle.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+                border.width: 1
+                radius: 2
+            }
+        }
+
+        Button {
+            id: marginToggle
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+            text: "Use Margin"
+            clip: true
+
+            onClicked: {
+                if (text === "Use Margin") {
+                    barGraph.barSeriesMargin = Qt.size(0.2, 0.2)
+                    barGraph.barSpacing = Qt.size(0.0, 0.0)
+                    text = "Use Spacing"
+                } else if (text === "Use Spacing") {
+                    barGraph.barSeriesMargin = Qt.size(0.0, 0.0)
+                    barGraph.barSpacing = Qt.size(0.5, 0.5)
+                    text = "Use Margin"
+                }
+            }
+            contentItem: Text {
+                text: marginToggle.text
+                opacity: marginToggle.enabled ? 1.0 : 0.3
+                color: barGraph.theme.labelTextColor
+                horizontalAlignment: Text.AlignHCenter
+                verticalAlignment: Text.AlignVCenter
+                elide: Text.ElideRight
+            }
+
+            background: Rectangle {
+                opacity: marginToggle.enabled ? 1 : 0.3
+                color: marginToggle.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+                border.color: marginToggle.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+                border.width: 1
+                radius: 2
+            }
+        }
+    }
+
+    Item {
+        id: dataView
+        anchors.right: mainview.right
+        anchors.bottom: mainview.bottom
+
+        Bars3D {
+            id: barGraph
+            anchors.fill: parent
+            shadowQuality: Graphs3D.ShadowQuality.SoftHigh
+            selectionMode: Graphs3D.SelectionFlag.Item
+            theme: GraphsTheme {
+                colorScheme: GraphsTheme.ColorScheme.Dark
+                labelBorderVisible: true
+                labelFont.pointSize: 35
+                labelBackgroundVisible: true
+                colorStyle: GraphsTheme.ColorStyle.RangeGradient
+                singleHighlightGradient: customGradient
+
+                Gradient {
+                    id: customGradient
+                    GradientStop { position: 1.0; color: "#FFFF00" }
+                    GradientStop { position: 0.0; color: "#808000" }
+                }
+            }
+            barThickness: 0.7
+            barSpacing: Qt.size(0.5, 0.5)
+            barSpacingRelative: false
+            cameraPreset: Graphs3D.CameraPreset.IsometricLeftHigh
+            columnAxis: graphAxes.column
+            rowAxis: graphAxes.row
+            valueAxis: graphAxes.value
+
+            //! [4]
+            Bar3DSeries {
+                id: secondarySeries
+                visible: false
+                itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel"
+                baseGradient: secondaryGradient
+
+                ItemModelBarDataProxy {
+                    id: secondaryProxy
+                    itemModel: graphData.model
+                    rowRole: "timestamp"
+                    columnRole: "timestamp"
+                    valueRole: "expenses"
+                    rowRolePattern: /^(\d\d\d\d).*$/
+                    columnRolePattern: /^.*-(\d\d)$/
+                    valueRolePattern: /-/
+                    rowRoleReplace: "\\1"
+                    columnRoleReplace: "\\1"
+                    multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
+                }
+                //! [4]
+
+                Gradient {
+                    id: secondaryGradient
+                    GradientStop { position: 1.0; color: "#FF0000" }
+                    GradientStop { position: 0.0; color: "#600000" }
+                }
+
+                onSelectedBarChanged: (position) => mainview.handleSelectionChange(secondarySeries,
+                                                                                   position)
+            }
+
+            //! [3]
+            Bar3DSeries {
+                id: barSeries
+                itemLabelFormat: "Income, @colLabel, @rowLabel: @valueLabel"
+                baseGradient: barGradient
+
+                ItemModelBarDataProxy {
+                    id: modelProxy
+                    itemModel: graphData.model
+                    rowRole: "timestamp"
+                    columnRole: "timestamp"
+                    valueRole: "income"
+                    rowRolePattern: /^(\d\d\d\d).*$/
+                    columnRolePattern: /^.*-(\d\d)$/
+                    rowRoleReplace: "\\1"
+                    columnRoleReplace: "\\1"
+                    multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
+                }
+                //! [3]
+
+                Gradient {
+                    id: barGradient
+                    GradientStop { position: 1.0; color: "#00FF00" }
+                    GradientStop { position: 0.0; color: "#006000" }
+                }
+
+                onSelectedBarChanged: (position) => mainview.handleSelectionChange(barSeries,
+                                                                                   position)
+            }
+        }
+    }
+
+    states: [
+        State  {
+            name: "landscape"
+            PropertyChanges {
+                target: dataView
+                width: mainview.width / 4 * 3
+                height: mainview.height
+            }
+            PropertyChanges  {
+                target: tableViewLayout
+                height: mainview.height - buttonLayoutHeight
+                anchors.right: dataView.left
+                anchors.left: mainview.left
+                anchors.bottom: undefined
+            }
+            PropertyChanges  {
+                target: controlLayout
+                width: mainview.width / 4
+                height: buttonLayoutHeight
+                anchors.top: tableViewLayout.bottom
+                anchors.bottom: mainview.bottom
+                anchors.left: mainview.left
+                anchors.right: dataView.left
+            }
+        },
+        State  {
+            name: "portrait"
+            PropertyChanges {
+                target: dataView
+                width: mainview.width
+                height: mainview.width
+            }
+            PropertyChanges  {
+                target: tableViewLayout
+                height: mainview.width
+                anchors.right: controlLayout.left
+                anchors.left: mainview.left
+                anchors.bottom: dataView.top
+            }
+            PropertyChanges  {
+                target: controlLayout
+                width: mainview.height / 4
+                height: mainview.width / 4
+                anchors.top: mainview.top
+                anchors.bottom: dataView.top
+                anchors.left: undefined
+                anchors.right: mainview.right
+            }
+        }
+    ]
+}
diff --git a/examples/graphs/3d/bars/Bars/qmldir b/examples/graphs/3d/bars/Bars/qmldir
new file mode 100644 (file)
index 0000000..d6ace35
--- /dev/null
@@ -0,0 +1,4 @@
+module Bars
+Main 1.0 Main.qml
+Axes 1.0 Axes.qml
+Data 1.0 Data.qml
diff --git a/examples/graphs/3d/bars/bars.pyproject b/examples/graphs/3d/bars/bars.pyproject
new file mode 100644 (file)
index 0000000..8662847
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "files": [
+        "main.py",
+        "Bars/Main.qml",
+        "Bars/Data.qml",
+        "Bars/Axes.qml",
+        "Bars/qmldir"
+    ]
+}
diff --git a/examples/graphs/3d/bars/doc/bars-example.webp b/examples/graphs/3d/bars/doc/bars-example.webp
new file mode 100644 (file)
index 0000000..8d55d57
Binary files /dev/null and b/examples/graphs/3d/bars/doc/bars-example.webp differ
diff --git a/examples/graphs/3d/bars/doc/bars.rst b/examples/graphs/3d/bars/doc/bars.rst
new file mode 100644 (file)
index 0000000..e36c543
--- /dev/null
@@ -0,0 +1,12 @@
+Simple Bar Graph
+================
+
+The Qt 3D Bar Graph example demonstrates creating a 3D bar graph in QML
+using Bars3D. It visualizes fictional company data for income and expenses
+over time, showcasing features like data series switching, custom axis labels,
+and interactive data selection.
+
+
+.. image:: bars-example.webp
+   :width: 400
+   :alt: Widget Screenshot
diff --git a/examples/graphs/3d/bars/main.py b/examples/graphs/3d/bars/main.py
new file mode 100644 (file)
index 0000000..3bd3d2b
--- /dev/null
@@ -0,0 +1,24 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+
+
+if __name__ == '__main__':
+    app = QGuiApplication(sys.argv)
+    view = QQuickView()
+
+    view.engine().addImportPath(Path(__file__).parent)
+    view.loadFromModule("Bars", "Main")
+    view.setTitle("Monthly income / expenses")
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
+    view.setColor("black")
+    view.show()
+
+    ex = app.exec()
+    del view
+    sys.exit(ex)
index 5fb4b44722fb68c9ec9cfb53e72ff69e3ae21c9f..efdb7d5503fa176d986de01b8613d2dff3ab4328 100644 (file)
@@ -1,14 +1,15 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
 from PySide6.QtCore import QSize
 from PySide6.QtGui import QVector3D
-from PySide6.QtGraphs import (Q3DSurface, QSurfaceDataItem,
-                              QSurface3DSeries)
-from PySide6.QtWidgets import QApplication
+from PySide6.QtGraphs import (QSurfaceDataItem, QSurface3DSeries)
+from PySide6.QtGraphsWidgets import (Q3DSurfaceWidgetItem)
 from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtWidgets import QApplication
 
 
 DESCRIPTION = """Minimal Qt Graphs Surface Example
@@ -22,7 +23,9 @@ if __name__ == '__main__':
 
     print(DESCRIPTION)
 
-    surface = Q3DSurface()
+    window = QQuickWidget()
+    surface = Q3DSurfaceWidgetItem()
+    surface.setWidget(window)
     axis = surface.axisX()
     axis.setTitle("X")
     axis.setTitleVisible(True)
@@ -47,8 +50,10 @@ if __name__ == '__main__':
 
     available_height = app.primaryScreen().availableGeometry().height()
     width = available_height * 4 / 5
-    surface.resize(QSize(width, width))
-    surface.setResizeMode(QQuickWidget.SizeRootObjectToView)
-    surface.show()
+    window.resize(QSize(width, width))
+    window.show()
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    surface = None
+    del window
+    sys.exit(exit_code)
diff --git a/examples/graphs/3d/minimalsurfacegraph/minimalsurfacegraph.pyproject b/examples/graphs/3d/minimalsurfacegraph/minimalsurfacegraph.pyproject
new file mode 100644 (file)
index 0000000..cc7a74a
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "files": ["main.py"]
+}
diff --git a/examples/graphs/3d/widgetgallery/axesinputhandler.py b/examples/graphs/3d/widgetgallery/axesinputhandler.py
deleted file mode 100644 (file)
index 4c42029..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from enum import Enum
-from math import sin, cos, degrees
-
-from PySide6.QtCore import Qt
-from PySide6.QtGraphs import QAbstract3DGraph, Q3DInputHandler
-
-
-class InputState(Enum):
-    StateNormal = 0
-    StateDraggingX = 1
-    StateDraggingZ = 2
-    StateDraggingY = 3
-
-
-class AxesInputHandler(Q3DInputHandler):
-
-    def __init__(self, graph, parent=None):
-        super().__init__(parent)
-        self._mousePressed = False
-        self._state = InputState.StateNormal
-        self._axisX = None
-        self._axisZ = None
-        self._axisY = None
-        self._speedModifier = 15.0
-
-        # Connect to the item selection signal from graph
-        graph.selectedElementChanged.connect(self.handleElementSelected)
-
-    def setAxes(self, axisX, axisZ, axisY):
-        self._axisX = axisX
-        self._axisZ = axisZ
-        self._axisY = axisY
-
-    def setDragSpeedModifier(self, modifier):
-        self._speedModifier = modifier
-
-    def mousePressEvent(self, event, mousePos):
-        super().mousePressEvent(event, mousePos)
-        if Qt.LeftButton == event.button():
-            self._mousePressed = True
-
-    def mouseMoveEvent(self, event, mousePos):
-        # Check if we're trying to drag axis label
-        if self._mousePressed and self._state != InputState.StateNormal:
-            self.setPreviousInputPos(self.inputPosition())
-            self.setInputPosition(mousePos)
-            self.handleAxisDragging()
-        else:
-            super().mouseMoveEvent(event, mousePos)
-
-    def mouseReleaseEvent(self, event, mousePos):
-        super().mouseReleaseEvent(event, mousePos)
-        self._mousePressed = False
-        self._state = InputState.StateNormal
-
-    def handleElementSelected(self, type):
-        if type == QAbstract3DGraph.ElementAxisXLabel:
-            self._state = InputState.StateDraggingX
-        elif type == QAbstract3DGraph.ElementAxisYLabel:
-            self._state = InputState.StateDraggingY
-        elif type == QAbstract3DGraph.ElementAxisZLabel:
-            self._state = InputState.StateDraggingZ
-        else:
-            self._state = InputState.StateNormal
-
-    def handleAxisDragging(self):
-        distance = 0.0
-        # Get scene orientation from active camera
-        xRotation = self.cameraXRotation()
-        yRotation = self.cameraYRotation()
-
-        # Calculate directional drag multipliers based on rotation
-        xMulX = cos(degrees(xRotation))
-        xMulY = sin(degrees(xRotation))
-        zMulX = sin(degrees(xRotation))
-        zMulY = cos(degrees(xRotation))
-
-        # Get the drag amount
-        move = self.inputPosition() - self.previousInputPos()
-
-        # Flip the effect of y movement if we're viewing from below
-        yMove = -move.y() if yRotation < 0 else move.y()
-
-        # Adjust axes
-        if self._state == InputState.StateDraggingX:
-            distance = (move.x() * xMulX - yMove * xMulY) / self._speedModifier
-            self._axisX.setRange(self._axisX.min() - distance,
-                                 self._axisX.max() - distance)
-        elif self._state == InputState.StateDraggingZ:
-            distance = (move.x() * zMulX + yMove * zMulY) / self._speedModifier
-            self._axisZ.setRange(self._axisZ.min() + distance,
-                                 self._axisZ.max() + distance)
-        elif self._state == InputState.StateDraggingY:
-            # No need to use adjusted y move here
-            distance = move.y() / self._speedModifier
-            self._axisY.setRange(self._axisY.min() + distance,
-                                 self._axisY.max() + distance)
diff --git a/examples/graphs/3d/widgetgallery/bargraph.py b/examples/graphs/3d/widgetgallery/bargraph.py
deleted file mode 100644 (file)
index 822acb4..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from graphmodifier import GraphModifier
-
-from PySide6.QtCore import QObject, Qt
-from PySide6.QtGui import QFont
-from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
-                               QLabel, QPushButton, QHBoxLayout, QSizePolicy,
-                               QRadioButton, QSlider, QVBoxLayout, QWidget)
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries, Q3DBars)
-
-
-class BarGraph(QObject):
-
-    def __init__(self, minimum_graph_size, maximum_graph_size):
-        super().__init__()
-        self._barsGraph = Q3DBars()
-        self._barsWidget = QWidget()
-        hLayout = QHBoxLayout(self._barsWidget)
-        self._barsGraph.setMinimumSize(minimum_graph_size)
-        self._barsGraph.setMaximumSize(maximum_graph_size)
-        self._barsGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
-        self._barsGraph.setFocusPolicy(Qt.StrongFocus)
-        self._barsGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
-        hLayout.addWidget(self._barsGraph, 1)
-
-        vLayout = QVBoxLayout()
-        hLayout.addLayout(vLayout)
-
-        themeList = QComboBox(self._barsWidget)
-        themeList.addItem("Qt")
-        themeList.addItem("Primary Colors")
-        themeList.addItem("Digia")
-        themeList.addItem("Stone Moss")
-        themeList.addItem("Army Blue")
-        themeList.addItem("Retro")
-        themeList.addItem("Ebony")
-        themeList.addItem("Isabelle")
-        themeList.setCurrentIndex(0)
-
-        labelButton = QPushButton(self._barsWidget)
-        labelButton.setText("Change label style")
-
-        smoothCheckBox = QCheckBox(self._barsWidget)
-        smoothCheckBox.setText("Smooth bars")
-        smoothCheckBox.setChecked(False)
-
-        barStyleList = QComboBox(self._barsWidget)
-        barStyleList.addItem("Bar", QAbstract3DSeries.Mesh.Bar)
-        barStyleList.addItem("Pyramid", QAbstract3DSeries.Mesh.Pyramid)
-        barStyleList.addItem("Cone", QAbstract3DSeries.Mesh.Cone)
-        barStyleList.addItem("Cylinder", QAbstract3DSeries.Mesh.Cylinder)
-        barStyleList.addItem("Bevel bar", QAbstract3DSeries.Mesh.BevelBar)
-        barStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
-        barStyleList.setCurrentIndex(4)
-
-        cameraButton = QPushButton(self._barsWidget)
-        cameraButton.setText("Change camera preset")
-
-        zoomToSelectedButton = QPushButton(self._barsWidget)
-        zoomToSelectedButton.setText("Zoom to selected bar")
-
-        selectionModeList = QComboBox(self._barsWidget)
-        selectionModeList.addItem("None", QAbstract3DGraph.SelectionNone)
-        selectionModeList.addItem("Bar", QAbstract3DGraph.SelectionItem)
-        selectionModeList.addItem("Row", QAbstract3DGraph.SelectionRow)
-        sel = QAbstract3DGraph.SelectionItemAndRow
-        selectionModeList.addItem("Bar and Row", sel)
-        selectionModeList.addItem("Column", QAbstract3DGraph.SelectionColumn)
-        sel = QAbstract3DGraph.SelectionItemAndColumn
-        selectionModeList.addItem("Bar and Column", sel)
-        sel = QAbstract3DGraph.SelectionRowAndColumn
-        selectionModeList.addItem("Row and Column", sel)
-        sel = QAbstract3DGraph.SelectionItemRowAndColumn
-        selectionModeList.addItem("Bar, Row and Column", sel)
-        sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionRow
-        selectionModeList.addItem("Slice into Row", sel)
-        sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow
-        selectionModeList.addItem("Slice into Row and Item", sel)
-        sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn
-        selectionModeList.addItem("Slice into Column", sel)
-        sel = (QAbstract3DGraph.SelectionSlice
-               | QAbstract3DGraph.SelectionItemAndColumn)
-        selectionModeList.addItem("Slice into Column and Item", sel)
-        sel = (QAbstract3DGraph.SelectionItemRowAndColumn
-               | QAbstract3DGraph.SelectionMultiSeries)
-        selectionModeList.addItem("Multi: Bar, Row, Col", sel)
-        sel = (QAbstract3DGraph.SelectionSlice
-               | QAbstract3DGraph.SelectionItemAndRow
-               | QAbstract3DGraph.SelectionMultiSeries)
-        selectionModeList.addItem("Multi, Slice: Row, Item", sel)
-        sel = (QAbstract3DGraph.SelectionSlice
-               | QAbstract3DGraph.SelectionItemAndColumn
-               | QAbstract3DGraph.SelectionMultiSeries)
-        selectionModeList.addItem("Multi, Slice: Col, Item", sel)
-        selectionModeList.setCurrentIndex(1)
-
-        backgroundCheckBox = QCheckBox(self._barsWidget)
-        backgroundCheckBox.setText("Show background")
-        backgroundCheckBox.setChecked(False)
-
-        gridCheckBox = QCheckBox(self._barsWidget)
-        gridCheckBox.setText("Show grid")
-        gridCheckBox.setChecked(True)
-
-        seriesCheckBox = QCheckBox(self._barsWidget)
-        seriesCheckBox.setText("Show second series")
-        seriesCheckBox.setChecked(False)
-
-        reverseValueAxisCheckBox = QCheckBox(self._barsWidget)
-        reverseValueAxisCheckBox.setText("Reverse value axis")
-        reverseValueAxisCheckBox.setChecked(False)
-
-        reflectionCheckBox = QCheckBox(self._barsWidget)
-        reflectionCheckBox.setText("Show reflections")
-        reflectionCheckBox.setChecked(False)
-
-        rotationSliderX = QSlider(Qt.Horizontal, self._barsWidget)
-        rotationSliderX.setTickInterval(30)
-        rotationSliderX.setTickPosition(QSlider.TicksBelow)
-        rotationSliderX.setMinimum(-180)
-        rotationSliderX.setValue(0)
-        rotationSliderX.setMaximum(180)
-        rotationSliderY = QSlider(Qt.Horizontal, self._barsWidget)
-        rotationSliderY.setTickInterval(15)
-        rotationSliderY.setTickPosition(QSlider.TicksAbove)
-        rotationSliderY.setMinimum(-90)
-        rotationSliderY.setValue(0)
-        rotationSliderY.setMaximum(90)
-
-        fontSizeSlider = QSlider(Qt.Horizontal, self._barsWidget)
-        fontSizeSlider.setTickInterval(10)
-        fontSizeSlider.setTickPosition(QSlider.TicksBelow)
-        fontSizeSlider.setMinimum(1)
-        fontSizeSlider.setValue(30)
-        fontSizeSlider.setMaximum(100)
-
-        fontList = QFontComboBox(self._barsWidget)
-        fontList.setCurrentFont(QFont("Times New Roman"))
-
-        shadowQuality = QComboBox(self._barsWidget)
-        shadowQuality.addItem("None")
-        shadowQuality.addItem("Low")
-        shadowQuality.addItem("Medium")
-        shadowQuality.addItem("High")
-        shadowQuality.addItem("Low Soft")
-        shadowQuality.addItem("Medium Soft")
-        shadowQuality.addItem("High Soft")
-        shadowQuality.setCurrentIndex(5)
-
-        rangeList = QComboBox(self._barsWidget)
-        rangeList.addItem("2015")
-        rangeList.addItem("2016")
-        rangeList.addItem("2017")
-        rangeList.addItem("2018")
-        rangeList.addItem("2019")
-        rangeList.addItem("2020")
-        rangeList.addItem("2021")
-        rangeList.addItem("2022")
-        rangeList.addItem("All")
-        rangeList.setCurrentIndex(8)
-
-        axisTitlesVisibleCB = QCheckBox(self._barsWidget)
-        axisTitlesVisibleCB.setText("Axis titles visible")
-        axisTitlesVisibleCB.setChecked(True)
-
-        axisTitlesFixedCB = QCheckBox(self._barsWidget)
-        axisTitlesFixedCB.setText("Axis titles fixed")
-        axisTitlesFixedCB.setChecked(True)
-
-        axisLabelRotationSlider = QSlider(Qt.Horizontal, self._barsWidget)
-        axisLabelRotationSlider.setTickInterval(10)
-        axisLabelRotationSlider.setTickPosition(QSlider.TicksBelow)
-        axisLabelRotationSlider.setMinimum(0)
-        axisLabelRotationSlider.setValue(30)
-        axisLabelRotationSlider.setMaximum(90)
-
-        modeGroup = QButtonGroup(self._barsWidget)
-        modeWeather = QRadioButton("Temperature Data", self._barsWidget)
-        modeWeather.setChecked(True)
-        modeCustomProxy = QRadioButton("Custom Proxy Data", self._barsWidget)
-        modeGroup.addButton(modeWeather)
-        modeGroup.addButton(modeCustomProxy)
-
-        vLayout.addWidget(QLabel("Rotate horizontally"))
-        vLayout.addWidget(rotationSliderX, 0, Qt.AlignTop)
-        vLayout.addWidget(QLabel("Rotate vertically"))
-        vLayout.addWidget(rotationSliderY, 0, Qt.AlignTop)
-        vLayout.addWidget(labelButton, 0, Qt.AlignTop)
-        vLayout.addWidget(cameraButton, 0, Qt.AlignTop)
-        vLayout.addWidget(zoomToSelectedButton, 0, Qt.AlignTop)
-        vLayout.addWidget(backgroundCheckBox)
-        vLayout.addWidget(gridCheckBox)
-        vLayout.addWidget(smoothCheckBox)
-        vLayout.addWidget(reflectionCheckBox)
-        vLayout.addWidget(seriesCheckBox)
-        vLayout.addWidget(reverseValueAxisCheckBox)
-        vLayout.addWidget(axisTitlesVisibleCB)
-        vLayout.addWidget(axisTitlesFixedCB)
-        vLayout.addWidget(QLabel("Show year"))
-        vLayout.addWidget(rangeList)
-        vLayout.addWidget(QLabel("Change bar style"))
-        vLayout.addWidget(barStyleList)
-        vLayout.addWidget(QLabel("Change selection mode"))
-        vLayout.addWidget(selectionModeList)
-        vLayout.addWidget(QLabel("Change theme"))
-        vLayout.addWidget(themeList)
-        vLayout.addWidget(QLabel("Adjust shadow quality"))
-        vLayout.addWidget(shadowQuality)
-        vLayout.addWidget(QLabel("Change font"))
-        vLayout.addWidget(fontList)
-        vLayout.addWidget(QLabel("Adjust font size"))
-        vLayout.addWidget(fontSizeSlider)
-        vLayout.addWidget(QLabel("Axis label rotation"))
-        vLayout.addWidget(axisLabelRotationSlider, 0, Qt.AlignTop)
-        vLayout.addWidget(modeWeather, 0, Qt.AlignTop)
-        vLayout.addWidget(modeCustomProxy, 1, Qt.AlignTop)
-
-        self._modifier = GraphModifier(self._barsGraph, self)
-
-        rotationSliderX.valueChanged.connect(self._modifier.rotateX)
-        rotationSliderY.valueChanged.connect(self._modifier.rotateY)
-
-        labelButton.clicked.connect(self._modifier.changeLabelBackground)
-        cameraButton.clicked.connect(self._modifier.changePresetCamera)
-        zoomToSelectedButton.clicked.connect(self._modifier.zoomToSelectedBar)
-
-        backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
-        gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
-        smoothCheckBox.stateChanged.connect(self._modifier.setSmoothBars)
-        seriesCheckBox.stateChanged.connect(self._modifier.setSeriesVisibility)
-        reverseValueAxisCheckBox.stateChanged.connect(self._modifier.setReverseValueAxis)
-        reflectionCheckBox.stateChanged.connect(self._modifier.setReflection)
-
-        self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
-        self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
-
-        rangeList.currentIndexChanged.connect(self._modifier.changeRange)
-
-        barStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
-
-        selectionModeList.currentIndexChanged.connect(self._modifier.changeSelectionMode)
-
-        themeList.currentIndexChanged.connect(self._modifier.changeTheme)
-
-        shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
-
-        self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
-        self._barsGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
-
-        fontSizeSlider.valueChanged.connect(self._modifier.changeFontSize)
-        fontList.currentFontChanged.connect(self._modifier.changeFont)
-
-        self._modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
-        self._modifier.fontChanged.connect(fontList.setCurrentFont)
-
-        axisTitlesVisibleCB.stateChanged.connect(self._modifier.setAxisTitleVisibility)
-        axisTitlesFixedCB.stateChanged.connect(self._modifier.setAxisTitleFixed)
-        axisLabelRotationSlider.valueChanged.connect(self._modifier.changeLabelRotation)
-
-        modeWeather.toggled.connect(self._modifier.setDataModeToWeather)
-        modeCustomProxy.toggled.connect(self._modifier.setDataModeToCustom)
-        modeWeather.toggled.connect(seriesCheckBox.setEnabled)
-        modeWeather.toggled.connect(rangeList.setEnabled)
-        modeWeather.toggled.connect(axisTitlesVisibleCB.setEnabled)
-        modeWeather.toggled.connect(axisTitlesFixedCB.setEnabled)
-        modeWeather.toggled.connect(axisLabelRotationSlider.setEnabled)
-
-    def barsWidget(self):
-        return self._barsWidget
diff --git a/examples/graphs/3d/widgetgallery/custominputhandler.py b/examples/graphs/3d/widgetgallery/custominputhandler.py
deleted file mode 100644 (file)
index 15fe00e..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from enum import Enum
-from math import sin, cos, degrees
-
-from PySide6.QtCore import Qt
-from PySide6.QtGraphs import (QAbstract3DGraph, Q3DInputHandler)
-
-
-class InputState(Enum):
-    StateNormal = 0
-    StateDraggingX = 1
-    StateDraggingZ = 2
-    StateDraggingY = 3
-
-
-class CustomInputHandler(Q3DInputHandler):
-
-    def __init__(self, graph, parent=None):
-        super().__init__(parent)
-        self._highlight = None
-        self._mousePressed = False
-        self._state = InputState.StateNormal
-        self._axisX = None
-        self._axisY = None
-        self._axisZ = None
-        self._speedModifier = 20.0
-        self._aspectRatio = 0.0
-        self._axisXMinValue = 0.0
-        self._axisXMaxValue = 0.0
-        self._axisXMinRange = 0.0
-        self._axisZMinValue = 0.0
-        self._axisZMaxValue = 0.0
-        self._axisZMinRange = 0.0
-        self._areaMinValue = 0.0
-        self._areaMaxValue = 0.0
-
-        # Connect to the item selection signal from graph
-        graph.selectedElementChanged.connect(self.handleElementSelected)
-
-    def setAspectRatio(self, ratio):
-        self._aspectRatio = ratio
-
-    def setHighlightSeries(self, series):
-        self._highlight = series
-
-    def setDragSpeedModifier(self, modifier):
-        self._speedModifier = modifier
-
-    def setLimits(self, min, max, minRange):
-        self._areaMinValue = min
-        self._areaMaxValue = max
-        self._axisXMinValue = self._areaMinValue
-        self._axisXMaxValue = self._areaMaxValue
-        self._axisZMinValue = self._areaMinValue
-        self._axisZMaxValue = self._areaMaxValue
-        self._axisXMinRange = minRange
-        self._axisZMinRange = minRange
-
-    def setAxes(self, axisX, axisY, axisZ):
-        self._axisX = axisX
-        self._axisY = axisY
-        self._axisZ = axisZ
-
-    def mousePressEvent(self, event, mousePos):
-        if Qt.LeftButton == event.button():
-            self._highlight.setVisible(False)
-            self._mousePressed = True
-        super().mousePressEvent(event, mousePos)
-
-    def wheelEvent(self, event):
-        delta = float(event.angleDelta().y())
-
-        self._axisXMinValue += delta
-        self._axisXMaxValue -= delta
-        self._axisZMinValue += delta
-        self._axisZMaxValue -= delta
-        self.checkConstraints()
-
-        y = (self._axisXMaxValue - self._axisXMinValue) * self._aspectRatio
-
-        self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
-        self._axisY.setRange(100.0, y)
-        self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
-
-    def mouseMoveEvent(self, event, mousePos):
-        # Check if we're trying to drag axis label
-        if self._mousePressed and self._state != InputState.StateNormal:
-            self.setPreviousInputPos(self.inputPosition())
-            self.setInputPosition(mousePos)
-            self.handleAxisDragging()
-        else:
-            super().mouseMoveEvent(event, mousePos)
-
-    def mouseReleaseEvent(self, event, mousePos):
-        super().mouseReleaseEvent(event, mousePos)
-        self._mousePressed = False
-        self._state = InputState.StateNormal
-
-    def handleElementSelected(self, type):
-        if type == QAbstract3DGraph.ElementAxisXLabel:
-            self._state = InputState.StateDraggingX
-        elif type == QAbstract3DGraph.ElementAxisZLabel:
-            self._state = InputState.StateDraggingZ
-        else:
-            self._state = InputState.StateNormal
-
-    def handleAxisDragging(self):
-        distance = 0.0
-
-        # Get scene orientation from active camera
-        xRotation = self.scene().cameraXRotation()
-
-        # Calculate directional drag multipliers based on rotation
-        xMulX = cos(degrees(xRotation))
-        xMulY = sin(degrees(xRotation))
-        zMulX = xMulY
-        zMulY = xMulX
-
-        # Get the drag amount
-        move = self.inputPosition() - self.previousInputPos()
-
-        # Adjust axes
-        if self._state == InputState.StateDraggingX:
-            distance = (move.x() * xMulX - move.y() * xMulY) * self._speedModifier
-            self._axisXMinValue -= distance
-            self._axisXMaxValue -= distance
-            if self._axisXMinValue < self._areaMinValue:
-                dist = self._axisXMaxValue - self._axisXMinValue
-                self._axisXMinValue = self._areaMinValue
-                self._axisXMaxValue = self._axisXMinValue + dist
-
-            if self._axisXMaxValue > self._areaMaxValue:
-                dist = self._axisXMaxValue - self._axisXMinValue
-                self._axisXMaxValue = self._areaMaxValue
-                self._axisXMinValue = self._axisXMaxValue - dist
-
-            self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
-        elif self._state == InputState.StateDraggingZ:
-            distance = (move.x() * zMulX + move.y() * zMulY) * self._speedModifier
-            self._axisZMinValue += distance
-            self._axisZMaxValue += distance
-            if self._axisZMinValue < self._areaMinValue:
-                dist = self._axisZMaxValue - self._axisZMinValue
-                self._axisZMinValue = self._areaMinValue
-                self._axisZMaxValue = self._axisZMinValue + dist
-
-            if self._axisZMaxValue > self._areaMaxValue:
-                dist = self._axisZMaxValue - self._axisZMinValue
-                self._axisZMaxValue = self._areaMaxValue
-                self._axisZMinValue = self._axisZMaxValue - dist
-
-            self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
-
-    def checkConstraints(self):
-        if self._axisXMinValue < self._areaMinValue:
-            self._axisXMinValue = self._areaMinValue
-        if self._axisXMaxValue > self._areaMaxValue:
-            self._axisXMaxValue = self._areaMaxValue
-        # Don't allow too much zoom in
-        range = self._axisXMaxValue - self._axisXMinValue
-        if range < self._axisXMinRange:
-            adjust = (self._axisXMinRange - range) / 2.0
-            self._axisXMinValue -= adjust
-            self._axisXMaxValue += adjust
-
-        if self._axisZMinValue < self._areaMinValue:
-            self._axisZMinValue = self._areaMinValue
-        if self._axisZMaxValue > self._areaMaxValue:
-            self._axisZMaxValue = self._areaMaxValue
-        # Don't allow too much zoom in
-        range = self._axisZMaxValue - self._axisZMinValue
-        if range < self._axisZMinRange:
-            adjust = (self._axisZMinRange - range) / 2.0
-            self._axisZMinValue -= adjust
-            self._axisZMaxValue += adjust
diff --git a/examples/graphs/3d/widgetgallery/data/layer_1.png b/examples/graphs/3d/widgetgallery/data/layer_1.png
deleted file mode 100644 (file)
index 9138c71..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/layer_1.png and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/layer_2.png b/examples/graphs/3d/widgetgallery/data/layer_2.png
deleted file mode 100644 (file)
index 61631ae..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/layer_2.png and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/layer_3.png b/examples/graphs/3d/widgetgallery/data/layer_3.png
deleted file mode 100644 (file)
index 066ffbe..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/layer_3.png and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/license.txt b/examples/graphs/3d/widgetgallery/data/license.txt
deleted file mode 100644 (file)
index 749daf3..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-License information regarding the data obtained from National Land Survey of
-Finland http://www.maanmittauslaitos.fi/en
-- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
-  U4422C) 08/2014
-- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
-
-National Land Survey open data licence - version 1.0 - 1 May 2012
-
-1. General information
-
-The National Land Survey of Finland (hereinafter the Licensor), as the holder
-of the immaterial rights to the data, has granted on the terms mentioned below
-the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
-of it).
-
-The Licensee is a natural or legal person who makes use of the data covered by
-this licence. The Licensee accepts the terms of this licence by receiving the
-dataset(s) covered by the licence.
-
-This Licence agreement does not create a co-operation or business relationship
-between the Licensee and the Licensor.
-
-2. Terms of the licence
-
-2.1. Right of use
-
-This licence grants a worldwide, free of charge and irrevocable parallel right
-of use to open data. According to the terms of the licence, data received by
-the Licensee can be freely:
- - copied, distributed and published,
- - modified and utilised commercially and non-commercially,
- - inserted into other products and
- - used as a part of a software application or service.
-
-2.2. Duties and responsibilities of the Licensee
-
-Through reasonable means suitable to the distribution medium or method which is
-used in conjunction with a product containing data or a service utilising data
-covered by this licence or while distributing data, the Licensee shall:
- - mention the name of the Licensor, the name of the dataset(s) and the time
-   when the National Land Survey has delivered the dataset(s) (e.g.: contains
-   data from the National Land Survey of Finland Topographic Database 06/2012)
- - provide a copy of this licence or a link to it, as well as
- - require third parties to provide the same information when granting rights
-   to copies of dataset(s) or products and services containing such data and
- - remove the name of the Licensor from the product or service, if required to
-   do so by the Licensor.
-
-The terms of this licence do not allow the Licensee to state in conjunction
-with the use of dataset(s) that the Licensor supports or recommends such use.
-
-2.3. Duties and responsibilities of the Licensor
-
-The Licensor shall ensure that
- - the Licensor has the right to grant rights to the dataset(s) in accordance
-   with this licence.
-
-The data has been licensed "as is" and the Licensor
- - shall not be held responsible for any errors or omissions in the data,
-   disclaims any warranty for the validity or up to date status of the data and
-   shall be free from liability for direct or consequential damages arising
-   from the use of data provided by the Licensor,
- - and is not obligated to ensure the continuous availability of the data, nor
-   to announce in advance the interruption or cessation of availability, and
-   the Licensor shall be free from liability for direct or consequential
-   damages arising from any such interruption or cessation.
-
-3. Jurisdiction
-
-Finnish law shall apply to this licence.
-
-4. Changes to this licence
-
-The Licensor may at any time change the terms of the licence or apply a
-different licence to the data. The terms of this licence shall, however, still
-apply to such data that has been received prior to the change of the terms of
-the licence or the licence itself.
diff --git a/examples/graphs/3d/widgetgallery/data/maptexture.jpg b/examples/graphs/3d/widgetgallery/data/maptexture.jpg
deleted file mode 100644 (file)
index ae5d66e..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/maptexture.jpg and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/narrowarrow.mesh b/examples/graphs/3d/widgetgallery/data/narrowarrow.mesh
deleted file mode 100644 (file)
index 288867b..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/narrowarrow.mesh and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/oilrig.mesh b/examples/graphs/3d/widgetgallery/data/oilrig.mesh
deleted file mode 100644 (file)
index 4a7baed..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/oilrig.mesh and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/pipe.mesh b/examples/graphs/3d/widgetgallery/data/pipe.mesh
deleted file mode 100644 (file)
index 984b6d4..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/pipe.mesh and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/raindata.txt b/examples/graphs/3d/widgetgallery/data/raindata.txt
deleted file mode 100644 (file)
index d955892..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu)
-# Format: year, month, rainfall
-2010,1, 0,
-2010,2, 3.4,
-2010,3, 52,
-2010,4, 33.8,
-2010,5, 45.6,
-2010,6, 43.8,
-2010,7, 104.6,
-2010,8, 105.4,
-2010,9, 107.2,
-2010,10,38.6,
-2010,11,17.8,
-2010,12,0,
-2011,1, 8.2,
-2011,2, 1.6,
-2011,3, 27.4,
-2011,4, 15.8,
-2011,5, 57.6,
-2011,6, 85.2,
-2011,7, 127,
-2011,8, 72.2,
-2011,9, 82.2,
-2011,10,62.4,
-2011,11,31.6,
-2011,12,53.8,
-2012,1, 0,
-2012,2, 5,
-2012,3, 32.4,
-2012,4, 57.6,
-2012,5, 71.4,
-2012,6, 60.8,
-2012,7, 109,
-2012,8, 43.6,
-2012,9, 79.4,
-2012,10,117.2,
-2012,11,59,
-2012,12,0.2,
-2013,1, 28,
-2013,2, 19,
-2013,3, 0,
-2013,4, 37.6,
-2013,5, 44.2,
-2013,6, 104.8,
-2013,7, 84.2,
-2013,8, 57.2,
-2013,9, 37.2,
-2013,10,64.6,
-2013,11,77.8,
-2013,12,92.8,
-2014,1, 23.8,
-2014,2, 23.6,
-2014,3, 15.4,
-2014,4, 13.2,
-2014,5, 36.4,
-2014,6, 26.4,
-2014,7, 95.8,
-2014,8, 81.8,
-2014,9, 13.8,
-2014,10,94.6,
-2014,11,44.6,
-2014,12,31,
-2015,1, 37.4,
-2015,2, 21,
-2015,3, 42,
-2015,4, 8.8,
-2015,5, 82.4,
-2015,6, 150,
-2015,7, 56.8,
-2015,8, 67.2,
-2015,9, 131.2,
-2015,10,38.4,
-2015,11,83.4,
-2015,12,47.8,
-2016,1, 12.4,
-2016,2, 34.8,
-2016,3, 29,
-2016,4, 40.4,
-2016,5, 32.4,
-2016,6, 80.2,
-2016,7, 102.6,
-2016,8, 95.6,
-2016,9, 40.2,
-2016,10,7.8,
-2016,11,39.6,
-2016,12,8.8,
-2017,1, 9.4,
-2017,2, 6.6,
-2017,3, 29,
-2017,4, 46.2,
-2017,5, 43.2,
-2017,6, 25.2,
-2017,7, 72.4,
-2017,8, 58.8,
-2017,9, 68.8,
-2017,10,45.8,
-2017,11,36.8,
-2017,12,29.6,
-2018,1, 19.8,
-2018,2, 0.8,
-2018,3, 4,
-2018,4, 23.2,
-2018,5, 13.2,
-2018,6, 62.8,
-2018,7, 33,
-2018,8, 96.6,
-2018,9, 72.6,
-2018,10,48.8,
-2018,11,31.8,
-2018,12,12.8,
-2019,1, 0.2,
-2019,2, 24.8,
-2019,3, 32,
-2019,4, 8.8,
-2019,5, 71.4,
-2019,6, 65.8,
-2019,7, 17.6,
-2019,8, 90,
-2019,9, 50,
-2019,10,77,
-2019,11,27,
-2019,12,43.2,
-2020,1, 28.8,
-2020,2, 45,
-2020,3, 18.6,
-2020,4, 13,
-2020,5, 30.8,
-2020,6, 21.4,
-2020,7, 163.6,
-2020,8, 12,
-2020,9, 102.4,
-2020,10,133.2,
-2020,11,69.8,
-2020,12,40.6,
-2021,1, 0.4,
-2021,2, 21.6,
-2021,3, 24,
-2021,4, 51.4,
-2021,5, 76.4,
-2021,6, 29.2,
-2021,7, 36.4,
-2021,8, 116,
-2021,9, 72.4,
-2021,10,93.4,
-2021,11,21,
-2021,12,10.2,
-2022,1, 8.6,
-2022,2, 6.6,
-2022,3, 5.2,
-2022,4, 15.2,
-2022,5, 37.6,
-2022,6, 45,
-2022,7, 67.4,
-2022,8, 161.6,
-2022,9, 22.8,
-2022,10,75.2,
-2022,11,21.8,
-2022,12,0.2
diff --git a/examples/graphs/3d/widgetgallery/data/refinery.mesh b/examples/graphs/3d/widgetgallery/data/refinery.mesh
deleted file mode 100644 (file)
index a7e2493..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/refinery.mesh and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/data/topography.png b/examples/graphs/3d/widgetgallery/data/topography.png
deleted file mode 100644 (file)
index 9349cdb..0000000
Binary files a/examples/graphs/3d/widgetgallery/data/topography.png and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/doc/widgetgallery.rst b/examples/graphs/3d/widgetgallery/doc/widgetgallery.rst
deleted file mode 100644 (file)
index 1470001..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Widget Gallery
-==============
-
-
-Widget Gallery demonstrates all three graph types and some of their special
-features. The graphs have their own tabs in the application.
-
-
-.. image:: widgetgallery.webp
-   :width: 400
-   :alt: Widget Screenshot
diff --git a/examples/graphs/3d/widgetgallery/doc/widgetgallery.webp b/examples/graphs/3d/widgetgallery/doc/widgetgallery.webp
deleted file mode 100644 (file)
index eb57672..0000000
Binary files a/examples/graphs/3d/widgetgallery/doc/widgetgallery.webp and /dev/null differ
diff --git a/examples/graphs/3d/widgetgallery/graphmodifier.py b/examples/graphs/3d/widgetgallery/graphmodifier.py
deleted file mode 100644 (file)
index 2eaafa7..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-from math import atan, degrees
-import numpy as np
-
-from PySide6.QtCore import QObject, QPropertyAnimation, Signal, Slot
-from PySide6.QtGui import QFont, QVector3D
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
-                              QBarDataItem, QBar3DSeries, QCategory3DAxis,
-                              QValue3DAxis, Q3DTheme)
-
-from rainfalldata import RainfallData
-
-# Set up data
-TEMP_OULU = np.array([
-    [-7.4, -2.4, 0.0, 3.0, 8.2, 11.6, 14.7, 15.4, 11.4, 4.2, 2.1, -2.3],  # 2015
-    [-13.4, -3.9, -1.8, 3.1, 10.6, 13.7, 17.8, 13.6, 10.7, 3.5, -3.1, -4.2],  # 2016
-    [-5.7, -6.7, -3.0, -0.1, 4.7, 12.4, 16.1, 14.1, 9.4, 3.0, -0.3, -3.2],  # 2017
-    [-6.4, -11.9, -7.4, 1.9, 11.4, 12.4, 21.5, 16.1, 11.0, 4.4, 2.1, -4.1],  # 2018
-    [-11.7, -6.1, -2.4, 3.9, 7.2, 14.5, 15.6, 14.4, 8.5, 2.0, -3.0, -1.5],  # 2019
-    [-2.1, -3.4, -1.8, 0.6, 7.0, 17.1, 15.6, 15.4, 11.1, 5.6, 1.9, -1.7],  # 2020
-    [-9.6, -11.6, -3.2, 2.4, 7.8, 17.3, 19.4, 14.2, 8.0, 5.2, -2.2, -8.6],  # 2021
-    [-7.3, -6.4, -1.8, 1.3, 8.1, 15.5, 17.6, 17.6, 9.1, 5.4, -1.5, -4.4]],  # 2022
-    np.float64)
-
-
-TEMP_HELSINKI = np.array([
-    [-2.0, -0.1, 1.8, 5.1, 9.7, 13.7, 16.3, 17.3, 12.7, 5.4, 4.6, 2.1],  # 2015
-    [-10.3, -0.6, 0.0, 4.9, 14.3, 15.7, 17.7, 16.0, 12.7, 4.6, -1.0, -0.9],  # 2016
-    [-2.9, -3.3, 0.7, 2.3, 9.9, 13.8, 16.1, 15.9, 11.4, 5.0, 2.7, 0.7],  # 2017
-    [-2.2, -8.4, -4.7, 5.0, 15.3, 15.8, 21.2, 18.2, 13.3, 6.7, 2.8, -2.0],  # 2018
-    [-6.2, -0.5, -0.3, 6.8, 10.6, 17.9, 17.5, 16.8, 11.3, 5.2, 1.8, 1.4],  # 2019
-    [1.9, 0.5, 1.7, 4.5, 9.5, 18.4, 16.5, 16.8, 13.0, 8.2, 4.4, 0.9],  # 2020
-    [-4.7, -8.1, -0.9, 4.5, 10.4, 19.2, 20.9, 15.4, 9.5, 8.0, 1.5, -6.7],  # 2021
-    [-3.3, -2.2, -0.2, 3.3, 9.6, 16.9, 18.1, 18.9, 9.2, 7.6, 2.3, -3.4]],  # 2022
-    np.float64)
-
-
-class GraphModifier(QObject):
-
-    shadowQualityChanged = Signal(int)
-    backgroundEnabledChanged = Signal(bool)
-    gridEnabledChanged = Signal(bool)
-    fontChanged = Signal(QFont)
-    fontSizeChanged = Signal(int)
-
-    def __init__(self, bargraph, parent):
-        super().__init__(parent)
-        self._graph = bargraph
-        self._temperatureAxis = QValue3DAxis()
-        self._yearAxis = QCategory3DAxis()
-        self._monthAxis = QCategory3DAxis()
-        self._primarySeries = QBar3DSeries()
-        self._secondarySeries = QBar3DSeries()
-        self._celsiusString = "°C"
-
-        self._xRotation = float(0)
-        self._yRotation = float(0)
-        self._fontSize = 30
-        self._segments = 4
-        self._subSegments = 3
-        self._minval = float(-20)
-        self._maxval = float(20)
-        self._barMesh = QAbstract3DSeries.Mesh.BevelBar
-        self._smooth = False
-        self._animationCameraX = QPropertyAnimation()
-        self._animationCameraY = QPropertyAnimation()
-        self._animationCameraZoom = QPropertyAnimation()
-        self._animationCameraTarget = QPropertyAnimation()
-        self._defaultAngleX = float(0)
-        self._defaultAngleY = float(0)
-        self._defaultZoom = float(0)
-        self._defaultTarget = []
-        self._customData = None
-
-        self._graph.setShadowQuality(QAbstract3DGraph.ShadowQuality.SoftMedium)
-        theme = self._graph.activeTheme()
-        theme.setBackgroundEnabled(False)
-        theme.setFont(QFont("Times New Roman", self._fontSize))
-        theme.setLabelBackgroundEnabled(True)
-        self._graph.setMultiSeriesUniform(True)
-
-        self._months = ["January", "February", "March", "April", "May", "June",
-                        "July", "August", "September", "October", "November",
-                        "December"]
-        self._years = ["2015", "2016", "2017", "2018", "2019", "2020",
-                       "2021", "2022"]
-
-        self._temperatureAxis.setTitle("Average temperature")
-        self._temperatureAxis.setSegmentCount(self._segments)
-        self._temperatureAxis.setSubSegmentCount(self._subSegments)
-        self._temperatureAxis.setRange(self._minval, self._maxval)
-        self._temperatureAxis.setLabelFormat("%.1f " + self._celsiusString)
-        self._temperatureAxis.setLabelAutoRotation(30.0)
-        self._temperatureAxis.setTitleVisible(True)
-
-        self._yearAxis.setTitle("Year")
-        self._yearAxis.setLabelAutoRotation(30.0)
-        self._yearAxis.setTitleVisible(True)
-        self._monthAxis.setTitle("Month")
-        self._monthAxis.setLabelAutoRotation(30.0)
-        self._monthAxis.setTitleVisible(True)
-
-        self._graph.setValueAxis(self._temperatureAxis)
-        self._graph.setRowAxis(self._yearAxis)
-        self._graph.setColumnAxis(self._monthAxis)
-
-        format = "Oulu - @colLabel @rowLabel: @valueLabel"
-        self._primarySeries.setItemLabelFormat(format)
-        self._primarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
-        self._primarySeries.setMeshSmooth(False)
-
-        format = "Helsinki - @colLabel @rowLabel: @valueLabel"
-        self._secondarySeries.setItemLabelFormat(format)
-        self._secondarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
-        self._secondarySeries.setMeshSmooth(False)
-        self._secondarySeries.setVisible(False)
-
-        self._graph.addSeries(self._primarySeries)
-        self._graph.addSeries(self._secondarySeries)
-
-        self.changePresetCamera()
-
-        self.resetTemperatureData()
-
-        # Set up property animations for zooming to the selected bar
-        self._defaultAngleX = self._graph.cameraXRotation()
-        self._defaultAngleY = self._graph.cameraYRotation()
-        self._defaultZoom = self._graph.cameraZoomLevel()
-        self._defaultTarget = self._graph.cameraTargetPosition()
-
-        self._animationCameraX.setTargetObject(self._graph)
-        self._animationCameraY.setTargetObject(self._graph)
-        self._animationCameraZoom.setTargetObject(self._graph)
-        self._animationCameraTarget.setTargetObject(self._graph)
-
-        self._animationCameraX.setPropertyName(b"cameraXRotation")
-        self._animationCameraY.setPropertyName(b"cameraYRotation")
-        self._animationCameraZoom.setPropertyName(b"cameraZoomLevel")
-        self._animationCameraTarget.setPropertyName(b"cameraTargetPosition")
-
-        duration = 1700
-        self._animationCameraX.setDuration(duration)
-        self._animationCameraY.setDuration(duration)
-        self._animationCameraZoom.setDuration(duration)
-        self._animationCameraTarget.setDuration(duration)
-
-        # The zoom always first zooms out above the graph and then zooms in
-        zoomOutFraction = 0.3
-        self._animationCameraX.setKeyValueAt(zoomOutFraction, 0.0)
-        self._animationCameraY.setKeyValueAt(zoomOutFraction, 90.0)
-        self._animationCameraZoom.setKeyValueAt(zoomOutFraction, 50.0)
-        self._animationCameraTarget.setKeyValueAt(zoomOutFraction,
-                                                  QVector3D(0, 0, 0))
-        self._customData = RainfallData()
-
-    def resetTemperatureData(self):
-        # Create data arrays
-        dataSet = []
-        dataSet2 = []
-
-        for year in range(0, len(self._years)):
-            # Create a data row
-            dataRow = []
-            dataRow2 = []
-            for month in range(0, len(self._months)):
-                # Add data to the row
-                item = QBarDataItem()
-                item.setValue(TEMP_OULU[year][month])
-                dataRow.append(item)
-                item = QBarDataItem()
-                item.setValue(TEMP_HELSINKI[year][month])
-                dataRow2.append(item)
-
-            # Add the row to the set
-            dataSet.append(dataRow)
-            dataSet2.append(dataRow2)
-
-        # Add data to the data proxy (the data proxy assumes ownership of it)
-        self._primarySeries.dataProxy().resetArray(dataSet, self._years, self._months)
-        self._secondarySeries.dataProxy().resetArray(dataSet2, self._years, self._months)
-
-    @Slot(int)
-    def changeRange(self, range):
-        if range >= len(self._years):
-            self._yearAxis.setRange(0, len(self._years) - 1)
-        else:
-            self._yearAxis.setRange(range, range)
-
-    @Slot(int)
-    def changeStyle(self, style):
-        comboBox = self.sender()
-        if comboBox:
-            self._barMesh = comboBox.itemData(style)
-            self._primarySeries.setMesh(self._barMesh)
-            self._secondarySeries.setMesh(self._barMesh)
-            self._customData.customSeries().setMesh(self._barMesh)
-
-    def changePresetCamera(self):
-        self._animationCameraX.stop()
-        self._animationCameraY.stop()
-        self._animationCameraZoom.stop()
-        self._animationCameraTarget.stop()
-
-        # Restore camera target in case animation has changed it
-        self._graph.setCameraTargetPosition(QVector3D(0.0, 0.0, 0.0))
-
-        self._preset = QAbstract3DGraph.CameraPreset.Front.value
-
-        self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset(self._preset))
-
-        self._preset += 1
-        if self._preset > QAbstract3DGraph.CameraPreset.DirectlyBelow.value:
-            self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
-
-    @Slot(int)
-    def changeTheme(self, theme):
-        currentTheme = self._graph.activeTheme()
-        currentTheme.setType(Q3DTheme.Theme(theme))
-        self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
-        self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
-        self.fontChanged.emit(currentTheme.font())
-        self.fontSizeChanged.emit(currentTheme.font().pointSize())
-
-    def changeLabelBackground(self):
-        theme = self._graph.activeTheme()
-        theme.setLabelBackgroundEnabled(not theme.isLabelBackgroundEnabled())
-
-    @Slot(int)
-    def changeSelectionMode(self, selectionMode):
-        comboBox = self.sender()
-        if comboBox:
-            flags = comboBox.itemData(selectionMode)
-            self._graph.setSelectionMode(QAbstract3DGraph.SelectionFlags(flags))
-
-    def changeFont(self, font):
-        newFont = font
-        self._graph.activeTheme().setFont(newFont)
-
-    def changeFontSize(self, fontsize):
-        self._fontSize = fontsize
-        font = self._graph.activeTheme().font()
-        font.setPointSize(self._fontSize)
-        self._graph.activeTheme().setFont(font)
-
-    @Slot(QAbstract3DGraph.ShadowQuality)
-    def shadowQualityUpdatedByVisual(self, sq):
-        # Updates the UI component to show correct shadow quality
-        self.shadowQualityChanged.emit(sq.value)
-
-    @Slot(int)
-    def changeLabelRotation(self, rotation):
-        self._temperatureAxis.setLabelAutoRotation(float(rotation))
-        self._monthAxis.setLabelAutoRotation(float(rotation))
-        self._yearAxis.setLabelAutoRotation(float(rotation))
-
-    @Slot(bool)
-    def setAxisTitleVisibility(self, enabled):
-        self._temperatureAxis.setTitleVisible(enabled)
-        self._monthAxis.setTitleVisible(enabled)
-        self._yearAxis.setTitleVisible(enabled)
-
-    @Slot(bool)
-    def setAxisTitleFixed(self, enabled):
-        self._temperatureAxis.setTitleFixed(enabled)
-        self._monthAxis.setTitleFixed(enabled)
-        self._yearAxis.setTitleFixed(enabled)
-
-    @Slot()
-    def zoomToSelectedBar(self):
-        self._animationCameraX.stop()
-        self._animationCameraY.stop()
-        self._animationCameraZoom.stop()
-        self._animationCameraTarget.stop()
-
-        currentX = self._graph.cameraXRotation()
-        currentY = self._graph.cameraYRotation()
-        currentZoom = self._graph.cameraZoomLevel()
-        currentTarget = self._graph.cameraTargetPosition()
-
-        self._animationCameraX.setStartValue(currentX)
-        self._animationCameraY.setStartValue(currentY)
-        self._animationCameraZoom.setStartValue(currentZoom)
-        self._animationCameraTarget.setStartValue(currentTarget)
-
-        selectedBar = (self._graph.selectedSeries().selectedBar()
-                       if self._graph.selectedSeries()
-                       else QBar3DSeries.invalidSelectionPosition())
-
-        if selectedBar != QBar3DSeries.invalidSelectionPosition():
-            # Normalize selected bar position within axis range to determine
-            # target coordinates
-            endTarget = QVector3D()
-            xMin = self._graph.columnAxis().min()
-            xRange = self._graph.columnAxis().max() - xMin
-            zMin = self._graph.rowAxis().min()
-            zRange = self._graph.rowAxis().max() - zMin
-            endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0 - 1.0)
-            endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0 - 1.0)
-
-            # Rotate the camera so that it always points approximately to the
-            # graph center
-            endAngleX = 90.0 - degrees(atan(float(endTarget.z() / endTarget.x())))
-            if endTarget.x() > 0.0:
-                endAngleX -= 180.0
-            proxy = self._graph.selectedSeries().dataProxy()
-            barValue = proxy.itemAt(selectedBar.x(), selectedBar.y()).value()
-            endAngleY = 30.0 if barValue >= 0.0 else -30.0
-            if self._graph.valueAxis().reversed():
-                endAngleY *= -1.0
-
-            self._animationCameraX.setEndValue(float(endAngleX))
-            self._animationCameraY.setEndValue(endAngleY)
-            self._animationCameraZoom.setEndValue(250)
-            self._animationCameraTarget.setEndValue(endTarget)
-        else:
-            # No selected bar, so return to the default view
-            self._animationCameraX.setEndValue(self._defaultAngleX)
-            self._animationCameraY.setEndValue(self._defaultAngleY)
-            self._animationCameraZoom.setEndValue(self._defaultZoom)
-            self._animationCameraTarget.setEndValue(self._defaultTarget)
-
-        self._animationCameraX.start()
-        self._animationCameraY.start()
-        self._animationCameraZoom.start()
-        self._animationCameraTarget.start()
-
-    @Slot(bool)
-    def setDataModeToWeather(self, enabled):
-        if enabled:
-            self.changeDataMode(False)
-
-    @Slot(bool)
-    def setDataModeToCustom(self, enabled):
-        if enabled:
-            self.changeDataMode(True)
-
-    def changeShadowQuality(self, quality):
-        sq = QAbstract3DGraph.ShadowQuality(quality)
-        self._graph.setShadowQuality(sq)
-        self.shadowQualityChanged.emit(quality)
-
-    def rotateX(self, rotation):
-        self._xRotation = rotation
-        camera = self._graph.scene().activeCamera()
-        camera.setCameraPosition(self._xRotation, self._yRotation)
-
-    def rotateY(self, rotation):
-        self._yRotation = rotation
-        camera = self._graph.scene().activeCamera()
-        camera.setCameraPosition(self._xRotation, self._yRotation)
-
-    def setBackgroundEnabled(self, enabled):
-        self._graph.activeTheme().setBackgroundEnabled(bool(enabled))
-
-    def setGridEnabled(self, enabled):
-        self._graph.activeTheme().setGridEnabled(bool(enabled))
-
-    def setSmoothBars(self, smooth):
-        self._smooth = bool(smooth)
-        self._primarySeries.setMeshSmooth(self._smooth)
-        self._secondarySeries.setMeshSmooth(self._smooth)
-        self._customData.customSeries().setMeshSmooth(self._smooth)
-
-    def setSeriesVisibility(self, enabled):
-        self._secondarySeries.setVisible(bool(enabled))
-
-    def setReverseValueAxis(self, enabled):
-        self._graph.valueAxis().setReversed(enabled)
-
-    def setReflection(self, enabled):
-        self._graph.setReflection(enabled)
-
-    def changeDataMode(self, customData):
-        # Change between weather data and data from custom proxy
-        if customData:
-            self._graph.removeSeries(self._primarySeries)
-            self._graph.removeSeries(self._secondarySeries)
-            self._graph.addSeries(self._customData.customSeries())
-            self._graph.setValueAxis(self._customData.valueAxis())
-            self._graph.setRowAxis(self._customData.rowAxis())
-            self._graph.setColumnAxis(self._customData.colAxis())
-        else:
-            self._graph.removeSeries(self._customData.customSeries())
-            self._graph.addSeries(self._primarySeries)
-            self._graph.addSeries(self._secondarySeries)
-            self._graph.setValueAxis(self._temperatureAxis)
-            self._graph.setRowAxis(self._yearAxis)
-            self._graph.setColumnAxis(self._monthAxis)
diff --git a/examples/graphs/3d/widgetgallery/highlightseries.py b/examples/graphs/3d/widgetgallery/highlightseries.py
deleted file mode 100644 (file)
index 8c7b916..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QPoint, Qt, Slot
-from PySide6.QtGui import QLinearGradient, QVector3D
-from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem, Q3DTheme)
-
-
-DARK_RED_POS = 1.0
-RED_POS = 0.8
-YELLOW_POS = 0.6
-GREEN_POS = 0.4
-DARK_GREEN_POS = 0.2
-
-
-class HighlightSeries(QSurface3DSeries):
-
-    def __init__(self):
-        super().__init__()
-        self._width = 100
-        self._height = 100
-        self._srcWidth = 0
-        self._srcHeight = 0
-        self._position = {}
-        self._topographicSeries = None
-        self._minHeight = 0.0
-        self.setDrawMode(QSurface3DSeries.DrawSurface)
-        self.setFlatShadingEnabled(True)
-        self.setVisible(False)
-
-    def setTopographicSeries(self, series):
-        self._topographicSeries = series
-        array = self._topographicSeries.dataProxy().array()
-        self._srcWidth = len(array[0])
-        self._srcHeight = len(array)
-        self._topographicSeries.selectedPointChanged.connect(self.handlePositionChange)
-
-    def setMinHeight(self, height):
-        self. m_minHeight = height
-
-    @Slot(QPoint)
-    def handlePositionChange(self, position):
-        self._position = position
-
-        if position == self.invalidSelectionPosition():
-            self.setVisible(False)
-            return
-
-        halfWidth = self._width / 2
-        halfHeight = self._height / 2
-
-        startX = position.y() - halfWidth
-        if startX < 0:
-            startX = 0
-        endX = position.y() + halfWidth
-        if endX > (self._srcWidth - 1):
-            endX = self._srcWidth - 1
-        startZ = position.x() - halfHeight
-        if startZ < 0:
-            startZ = 0
-        endZ = position.x() + halfHeight
-        if endZ > (self._srcHeight - 1):
-            endZ = self._srcHeight - 1
-
-        srcProxy = self._topographicSeries.dataProxy()
-        srcArray = srcProxy.array()
-
-        dataArray = []
-        for i in range(int(startZ), int(endZ)):
-            newRow = []
-            srcRow = srcArray[i]
-            for j in range(startX, endX):
-                pos = srcRow.at(j).position()
-                pos.setY(pos.y() + 0.1)
-                item = QSurfaceDataItem(QVector3D(pos))
-                newRow.append(item)
-            dataArray.append(newRow)
-        self.dataProxy().resetArray(dataArray)
-        self.setVisible(True)
-
-    @Slot(float)
-    def handleGradientChange(self, value):
-        ratio = self._minHeight / value
-
-        gr = QLinearGradient()
-        gr.setColorAt(0.0, Qt.black)
-        gr.setColorAt(DARK_GREEN_POS * ratio, Qt.darkGreen)
-        gr.setColorAt(GREEN_POS * ratio, Qt.green)
-        gr.setColorAt(YELLOW_POS * ratio, Qt.yellow)
-        gr.setColorAt(RED_POS * ratio, Qt.red)
-        gr.setColorAt(DARK_RED_POS * ratio, Qt.darkRed)
-
-        self.setBaseGradient(gr)
-        self.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
diff --git a/examples/graphs/3d/widgetgallery/main.py b/examples/graphs/3d/widgetgallery/main.py
deleted file mode 100644 (file)
index 7bb2238..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-"""PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
-
-import sys
-
-from PySide6.QtCore import QSize
-from PySide6.QtWidgets import QApplication, QTabWidget
-
-from bargraph import BarGraph
-from scattergraph import ScatterGraph
-from surfacegraph import SurfaceGraph
-
-
-if __name__ == "__main__":
-    app = QApplication(sys.argv)
-
-    # Create a tab widget for creating own tabs for Q3DBars, Q3DScatter, and Q3DSurface
-    tabWidget = QTabWidget()
-    tabWidget.setWindowTitle("Widget Gallery")
-
-    screen_size = tabWidget.screen().size()
-    minimum_graph_size = QSize(screen_size.width() / 2, screen_size.height() / 1.75)
-
-    # Create bar graph
-    bars = BarGraph(minimum_graph_size, screen_size)
-    # Create scatter graph
-    scatter = ScatterGraph(minimum_graph_size, screen_size)
-    # Create surface graph
-    surface = SurfaceGraph(minimum_graph_size, screen_size)
-
-    # Add bars widget
-    tabWidget.addTab(bars.barsWidget(), "Bar Graph")
-    # Add scatter widget
-    tabWidget.addTab(scatter.scatterWidget(), "Scatter Graph")
-    # Add surface widget
-    tabWidget.addTab(surface.surfaceWidget(), "Surface Graph")
-
-    tabWidget.show()
-    sys.exit(app.exec())
diff --git a/examples/graphs/3d/widgetgallery/rainfalldata.py b/examples/graphs/3d/widgetgallery/rainfalldata.py
deleted file mode 100644 (file)
index d74f45a..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import sys
-
-from pathlib import Path
-
-from PySide6.QtCore import QFile, QIODevice, QObject
-from PySide6.QtGraphs import (QBar3DSeries, QCategory3DAxis, QValue3DAxis)
-
-from variantbardataproxy import VariantBarDataProxy
-from variantbardatamapping import VariantBarDataMapping
-from variantdataset import VariantDataSet
-
-
-MONTHS = ["January", "February", "March", "April",
-          "May", "June", "July", "August", "September", "October",
-          "November", "December"]
-
-
-class RainfallData(QObject):
-
-    def __init__(self):
-        super().__init__()
-        self._columnCount = 0
-        self._rowCount = 0
-        self._years = []
-        self._numericMonths = []
-        self._proxy = VariantBarDataProxy()
-        self._mapping = None
-        self._dataSet = None
-        self._series = QBar3DSeries()
-        self._valueAxis = QValue3DAxis()
-        self._rowAxis = QCategory3DAxis()
-        self._colAxis = QCategory3DAxis()
-
-        # In data file the months are in numeric format, so create custom list
-        for i in range(1, 13):
-            self._numericMonths.append(str(i))
-
-        self._columnCount = len(self._numericMonths)
-
-        self.updateYearsList(2010, 2022)
-
-        # Create proxy and series
-        self._proxy = VariantBarDataProxy()
-        self._series = QBar3DSeries(self._proxy)
-
-        self._series.setItemLabelFormat("%.1f mm")
-
-        # Create the axes
-        self._rowAxis = QCategory3DAxis(self)
-        self._colAxis = QCategory3DAxis(self)
-        self._valueAxis = QValue3DAxis(self)
-        self._rowAxis.setAutoAdjustRange(True)
-        self._colAxis.setAutoAdjustRange(True)
-        self._valueAxis.setAutoAdjustRange(True)
-
-        # Set axis labels and titles
-        self._rowAxis.setTitle("Year")
-        self._colAxis.setTitle("Month")
-        self._valueAxis.setTitle("rainfall (mm)")
-        self._valueAxis.setSegmentCount(5)
-        self._rowAxis.setLabels(self._years)
-        self._colAxis.setLabels(MONTHS)
-        self._rowAxis.setTitleVisible(True)
-        self._colAxis.setTitleVisible(True)
-        self._valueAxis.setTitleVisible(True)
-
-        self.addDataSet()
-
-    def customSeries(self):
-        return self._series
-
-    def valueAxis(self):
-        return self._valueAxis
-
-    def rowAxis(self):
-        return self._rowAxis
-
-    def colAxis(self):
-        return self._colAxis
-
-    def updateYearsList(self, start, end):
-        self._years.clear()
-        for i in range(start, end + 1):
-            self._years.append(str(i))
-        self._rowCount = len(self._years)
-
-    def addDataSet(self):
-        # Create a new variant data set and data item list
-        self._dataSet = VariantDataSet()
-        itemList = []
-
-        # Read data from a data file into the data item list
-        file_path = Path(__file__).resolve().parent / "data" / "raindata.txt"
-        dataFile = QFile(file_path)
-        if dataFile.open(QIODevice.ReadOnly | QIODevice.Text):
-            data = dataFile.readAll().data().decode("utf8")
-            for line in data.split("\n"):
-                if line and not line.startswith("#"):  # Ignore comments
-                    tokens = line.split(",")
-                    # Each line has three data items: Year, month, and
-                    # rainfall value
-                    if len(tokens) >= 3:
-                        # Store year and month as strings, and rainfall value
-                        # as double into a variant data item and add the item to
-                        # the item list.
-                        newItem = []
-                        newItem.append(tokens[0].strip())
-                        newItem.append(tokens[1].strip())
-                        newItem.append(float(tokens[2].strip()))
-                        itemList.append(newItem)
-        else:
-            print("Unable to open data file:", dataFile.fileName(),
-                  file=sys.stderr)
-
-        # Add items to the data set and set it to the proxy
-        self._dataSet.addItems(itemList)
-        self._proxy.setDataSet(self._dataSet)
-
-        # Create new mapping for the data and set it to the proxy
-        self._mapping = VariantBarDataMapping(0, 1, 2,
-                                              self._years, self._numericMonths)
-        self._proxy.setMapping(self._mapping)
diff --git a/examples/graphs/3d/widgetgallery/scatterdatamodifier.py b/examples/graphs/3d/widgetgallery/scatterdatamodifier.py
deleted file mode 100644 (file)
index 15064b4..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from math import cos, degrees, sqrt
-
-from PySide6.QtCore import QObject, Signal, Slot, Qt
-from PySide6.QtGui import QVector3D
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
-                              QScatterDataItem, QScatterDataProxy,
-                              QScatter3DSeries, Q3DTheme)
-
-from axesinputhandler import AxesInputHandler
-
-
-NUMBER_OF_ITEMS = 10000
-CURVE_DIVIDER = 7.5
-LOWER_NUMBER_OF_ITEMS = 900
-LOWER_CURVE_DIVIDER = 0.75
-
-
-class ScatterDataModifier(QObject):
-
-    backgroundEnabledChanged = Signal(bool)
-    gridEnabledChanged = Signal(bool)
-    shadowQualityChanged = Signal(int)
-
-    def __init__(self, scatter, parent):
-        super().__init__(parent)
-
-        self._graph = scatter
-
-        self._style = QAbstract3DSeries.Mesh.Sphere
-        self._smooth = True
-        self._inputHandler = AxesInputHandler(scatter)
-        self._autoAdjust = True
-        self._itemCount = LOWER_NUMBER_OF_ITEMS
-        self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
-        self._inputHandler = AxesInputHandler(scatter)
-
-        self._graph.activeTheme().setType(Q3DTheme.Theme.StoneMoss)
-        self._graph.setShadowQuality(QAbstract3DGraph.ShadowQuality.SoftHigh)
-        self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset.Front)
-        self._graph.setCameraZoomLevel(80.0)
-
-        self._proxy = QScatterDataProxy()
-        self._series = QScatter3DSeries(self._proxy)
-        self._series.setItemLabelFormat("@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel")
-        self._series.setMeshSmooth(self._smooth)
-        self._graph.addSeries(self._series)
-        self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
-
-        # Give ownership of the handler to the graph and make it the active
-        # handler
-        self._graph.setActiveInputHandler(self._inputHandler)
-
-        # Give our axes to the input handler
-        self._inputHandler.setAxes(self._graph.axisX(), self._graph.axisZ(),
-                                   self._graph.axisY())
-
-        self.addData()
-
-    def addData(self):
-        # Configure the axes according to the data
-        self._graph.axisX().setTitle("X")
-        self._graph.axisY().setTitle("Y")
-        self._graph.axisZ().setTitle("Z")
-
-        dataArray = []
-        limit = int(sqrt(self._itemCount) / 2.0)
-        for i in range(-limit, limit):
-            for j in range(-limit, limit):
-                x = float(i) + 0.5
-                y = cos(degrees(float(i * j) / self._CURVE_DIVIDER))
-                z = float(j) + 0.5
-                dataArray.append(QScatterDataItem(QVector3D(x, y, z)))
-
-        self._graph.seriesList()[0].dataProxy().resetArray(dataArray)
-
-    @Slot(int)
-    def changeStyle(self, style):
-        comboBox = self.sender()
-        if comboBox:
-            self._style = comboBox.itemData(style)
-            if self._graph.seriesList():
-                self._graph.seriesList()[0].setMesh(self._style)
-
-    @Slot(int)
-    def setSmoothDots(self, smooth):
-        self._smooth = smooth == Qt.Checked.value
-        series = self._graph.seriesList()[0]
-        series.setMeshSmooth(self._smooth)
-
-    @Slot(int)
-    def changeTheme(self, theme):
-        currentTheme = self._graph.activeTheme()
-        currentTheme.setType(Q3DTheme.Theme(theme))
-        self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
-        self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
-
-    @Slot()
-    def changePresetCamera(self):
-        camera = self._graph.scene().activeCamera()
-        camera.setCameraPreset(QAbstract3DGraph.CameraPreset(self._preset))
-
-        self._preset += 1
-        if self._preset > QAbstract3DGraph.CameraPreset.DirectlyBelow.value:
-            self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
-
-    @Slot(QAbstract3DGraph.ShadowQuality)
-    def shadowQualityUpdatedByVisual(self, sq):
-        self.shadowQualityChanged.emit(sq.value)
-
-    @Slot(int)
-    def changeShadowQuality(self, quality):
-        sq = QAbstract3DGraph.ShadowQuality(quality)
-        self._graph.setShadowQuality(sq)
-
-    @Slot(int)
-    def setBackgroundEnabled(self, enabled):
-        self._graph.activeTheme().setBackgroundEnabled(enabled == Qt.Checked.value)
-
-    @Slot(int)
-    def setGridEnabled(self, enabled):
-        self._graph.activeTheme().setGridEnabled(enabled == Qt.Checked.value)
-
-    @Slot()
-    def toggleItemCount(self):
-        if self._itemCount == NUMBER_OF_ITEMS:
-            self._itemCount = LOWER_NUMBER_OF_ITEMS
-            self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
-        else:
-            self._itemCount = NUMBER_OF_ITEMS
-            self._CURVE_DIVIDER = CURVE_DIVIDER
-
-        self._graph.seriesList()[0].dataProxy().resetArray([])
-        self.addData()
-
-    @Slot()
-    def toggleRanges(self):
-        if not self._autoAdjust:
-            self._graph.axisX().setAutoAdjustRange(True)
-            self._graph.axisZ().setAutoAdjustRange(True)
-            self._inputHandler.setDragSpeedModifier(1.5)
-            self._autoAdjust = True
-        else:
-            self._graph.axisX().setRange(-10.0, 10.0)
-            self._graph.axisZ().setRange(-10.0, 10.0)
-            self._inputHandler.setDragSpeedModifier(15.0)
-            self._autoAdjust = False
diff --git a/examples/graphs/3d/widgetgallery/scattergraph.py b/examples/graphs/3d/widgetgallery/scattergraph.py
deleted file mode 100644 (file)
index 79e8933..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, QSize, Qt
-from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
-                               QLabel, QHBoxLayout, QSizePolicy,
-                               QVBoxLayout, QWidget, )
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import (QAbstract3DSeries, Q3DScatter)
-
-from scatterdatamodifier import ScatterDataModifier
-
-
-class ScatterGraph(QObject):
-
-    def __init__(self, minimum_graph_size, maximum_graph_size):
-        super().__init__()
-        self._scatterGraph = Q3DScatter()
-        self._scatterWidget = QWidget()
-        hLayout = QHBoxLayout(self._scatterWidget)
-        self._scatterGraph.setMinimumSize(minimum_graph_size)
-        self._scatterGraph.setMaximumSize(maximum_graph_size)
-        self._scatterGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
-        self._scatterGraph.setFocusPolicy(Qt.StrongFocus)
-        self._scatterGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
-        hLayout.addWidget(self._scatterGraph, 1)
-
-        vLayout = QVBoxLayout()
-        hLayout.addLayout(vLayout)
-
-        cameraButton = QCommandLinkButton(self._scatterWidget)
-        cameraButton.setText("Change camera preset")
-        cameraButton.setDescription("Switch between a number of preset camera positions")
-        cameraButton.setIconSize(QSize(0, 0))
-
-        itemCountButton = QCommandLinkButton(self._scatterWidget)
-        itemCountButton.setText("Toggle item count")
-        itemCountButton.setDescription("Switch between 900 and 10000 data points")
-        itemCountButton.setIconSize(QSize(0, 0))
-
-        rangeButton = QCommandLinkButton(self._scatterWidget)
-        rangeButton.setText("Toggle axis ranges")
-        rangeButton.setDescription("Switch between automatic axis ranges and preset ranges")
-        rangeButton.setIconSize(QSize(0, 0))
-
-        backgroundCheckBox = QCheckBox(self._scatterWidget)
-        backgroundCheckBox.setText("Show background")
-        backgroundCheckBox.setChecked(True)
-
-        gridCheckBox = QCheckBox(self._scatterWidget)
-        gridCheckBox.setText("Show grid")
-        gridCheckBox.setChecked(True)
-
-        smoothCheckBox = QCheckBox(self._scatterWidget)
-        smoothCheckBox.setText("Smooth dots")
-        smoothCheckBox.setChecked(True)
-
-        itemStyleList = QComboBox(self._scatterWidget)
-        itemStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
-        itemStyleList.addItem("Cube", QAbstract3DSeries.Mesh.Cube)
-        itemStyleList.addItem("Minimal", QAbstract3DSeries.Mesh.Minimal)
-        itemStyleList.addItem("Point", QAbstract3DSeries.Mesh.Point)
-        itemStyleList.setCurrentIndex(0)
-
-        themeList = QComboBox(self._scatterWidget)
-        themeList.addItem("Qt")
-        themeList.addItem("Primary Colors")
-        themeList.addItem("Digia")
-        themeList.addItem("Stone Moss")
-        themeList.addItem("Army Blue")
-        themeList.addItem("Retro")
-        themeList.addItem("Ebony")
-        themeList.addItem("Isabelle")
-        themeList.setCurrentIndex(3)
-
-        shadowQuality = QComboBox(self._scatterWidget)
-        shadowQuality.addItem("None")
-        shadowQuality.addItem("Low")
-        shadowQuality.addItem("Medium")
-        shadowQuality.addItem("High")
-        shadowQuality.addItem("Low Soft")
-        shadowQuality.addItem("Medium Soft")
-        shadowQuality.addItem("High Soft")
-        shadowQuality.setCurrentIndex(6)
-
-        vLayout.addWidget(cameraButton)
-        vLayout.addWidget(itemCountButton)
-        vLayout.addWidget(rangeButton)
-        vLayout.addWidget(backgroundCheckBox)
-        vLayout.addWidget(gridCheckBox)
-        vLayout.addWidget(smoothCheckBox)
-        vLayout.addWidget(QLabel("Change dot style"))
-        vLayout.addWidget(itemStyleList)
-        vLayout.addWidget(QLabel("Change theme"))
-        vLayout.addWidget(themeList)
-        vLayout.addWidget(QLabel("Adjust shadow quality"))
-        vLayout.addWidget(shadowQuality, 1, Qt.AlignTop)
-
-        self._modifier = ScatterDataModifier(self._scatterGraph, self)
-
-        cameraButton.clicked.connect(self._modifier.changePresetCamera)
-        itemCountButton.clicked.connect(self._modifier.toggleItemCount)
-        rangeButton.clicked.connect(self._modifier.toggleRanges)
-
-        backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
-        gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
-        smoothCheckBox.stateChanged.connect(self._modifier.setSmoothDots)
-
-        self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
-        self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
-        itemStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
-
-        themeList.currentIndexChanged.connect(self._modifier.changeTheme)
-
-        shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
-
-        self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
-        self._scatterGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
-
-    def scatterWidget(self):
-        return self._scatterWidget
diff --git a/examples/graphs/3d/widgetgallery/surfacegraph.py b/examples/graphs/3d/widgetgallery/surfacegraph.py
deleted file mode 100644 (file)
index 4052da8..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from surfacegraphmodifier import SurfaceGraphModifier
-
-from PySide6.QtCore import QObject, Qt
-from PySide6.QtGui import QBrush, QIcon, QLinearGradient, QPainter, QPixmap
-from PySide6.QtWidgets import (QGroupBox, QCheckBox, QLabel, QHBoxLayout,
-                               QPushButton, QRadioButton, QSizePolicy, QSlider,
-                               QVBoxLayout, QWidget)
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import Q3DSurface
-
-
-def gradientBtoYPB_Pixmap():
-    grBtoY = QLinearGradient(0, 0, 1, 100)
-    grBtoY.setColorAt(1.0, Qt.black)
-    grBtoY.setColorAt(0.67, Qt.blue)
-    grBtoY.setColorAt(0.33, Qt.red)
-    grBtoY.setColorAt(0.0, Qt.yellow)
-    pm = QPixmap(24, 100)
-    with QPainter(pm) as pmp:
-        pmp.setBrush(QBrush(grBtoY))
-        pmp.setPen(Qt.NoPen)
-        pmp.drawRect(0, 0, 24, 100)
-    return pm
-
-
-def gradientGtoRPB_Pixmap():
-    grGtoR = QLinearGradient(0, 0, 1, 100)
-    grGtoR.setColorAt(1.0, Qt.darkGreen)
-    grGtoR.setColorAt(0.5, Qt.yellow)
-    grGtoR.setColorAt(0.2, Qt.red)
-    grGtoR.setColorAt(0.0, Qt.darkRed)
-    pm = QPixmap(24, 100)
-    with QPainter(pm) as pmp:
-        pmp.setBrush(QBrush(grGtoR))
-        pmp.setPen(Qt.NoPen)
-        pmp.drawRect(0, 0, 24, 100)
-    return pm
-
-
-def highlightPixmap():
-    HEIGHT = 400
-    WIDTH = 110
-    BORDER = 10
-    gr = QLinearGradient(0, 0, 1, HEIGHT - 2 * BORDER)
-    gr.setColorAt(1.0, Qt.black)
-    gr.setColorAt(0.8, Qt.darkGreen)
-    gr.setColorAt(0.6, Qt.green)
-    gr.setColorAt(0.4, Qt.yellow)
-    gr.setColorAt(0.2, Qt.red)
-    gr.setColorAt(0.0, Qt.darkRed)
-    pmHighlight = QPixmap(WIDTH, HEIGHT)
-    pmHighlight.fill(Qt.transparent)
-    with QPainter(pmHighlight) as pmpHighlight:
-        pmpHighlight.setBrush(QBrush(gr))
-        pmpHighlight.setPen(Qt.NoPen)
-        pmpHighlight.drawRect(BORDER, BORDER, 35, HEIGHT - 2 * BORDER)
-        pmpHighlight.setPen(Qt.black)
-        step = (HEIGHT - 2 * BORDER) / 5
-        for i in range(0, 6):
-            yPos = i * step + BORDER
-            pmpHighlight.drawLine(BORDER, yPos, 55, yPos)
-            HEIGHT = 550 - (i * 110)
-            pmpHighlight.drawText(60, yPos + 2, f"{HEIGHT} m")
-    return pmHighlight
-
-
-class SurfaceGraph(QObject):
-
-    def __init__(self, minimum_graph_size, maximum_graph_size):
-        super().__init__()
-        self._surfaceGraph = Q3DSurface()
-        self._surfaceWidget = QWidget()
-        hLayout = QHBoxLayout(self._surfaceWidget)
-        self._surfaceGraph.setMinimumSize(minimum_graph_size)
-        self._surfaceGraph.setMaximumSize(maximum_graph_size)
-        self._surfaceGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
-        self._surfaceGraph.setFocusPolicy(Qt.StrongFocus)
-        self._surfaceGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
-        hLayout.addWidget(self._surfaceGraph, 1)
-        vLayout = QVBoxLayout()
-        hLayout.addLayout(vLayout)
-        vLayout.setAlignment(Qt.AlignTop)
-        # Create control widgets
-        modelGroupBox = QGroupBox("Model")
-        sqrtSinModelRB = QRadioButton(self._surfaceWidget)
-        sqrtSinModelRB.setText("Sqrt and Sin")
-        sqrtSinModelRB.setChecked(False)
-        heightMapModelRB = QRadioButton(self._surfaceWidget)
-        heightMapModelRB.setText("Multiseries\nHeight Map")
-        heightMapModelRB.setChecked(False)
-        texturedModelRB = QRadioButton(self._surfaceWidget)
-        texturedModelRB.setText("Textured\nTopography")
-        texturedModelRB.setChecked(False)
-        modelVBox = QVBoxLayout()
-        modelVBox.addWidget(sqrtSinModelRB)
-        modelVBox.addWidget(heightMapModelRB)
-        modelVBox.addWidget(texturedModelRB)
-        modelGroupBox.setLayout(modelVBox)
-        selectionGroupBox = QGroupBox("Graph Selection Mode")
-        modeNoneRB = QRadioButton(self._surfaceWidget)
-        modeNoneRB.setText("No selection")
-        modeNoneRB.setChecked(False)
-        modeItemRB = QRadioButton(self._surfaceWidget)
-        modeItemRB.setText("Item")
-        modeItemRB.setChecked(False)
-        modeSliceRowRB = QRadioButton(self._surfaceWidget)
-        modeSliceRowRB.setText("Row Slice")
-        modeSliceRowRB.setChecked(False)
-        modeSliceColumnRB = QRadioButton(self._surfaceWidget)
-        modeSliceColumnRB.setText("Column Slice")
-        modeSliceColumnRB.setChecked(False)
-        selectionVBox = QVBoxLayout()
-        selectionVBox.addWidget(modeNoneRB)
-        selectionVBox.addWidget(modeItemRB)
-        selectionVBox.addWidget(modeSliceRowRB)
-        selectionVBox.addWidget(modeSliceColumnRB)
-        selectionGroupBox.setLayout(selectionVBox)
-        axisGroupBox = QGroupBox("Axis ranges")
-        axisMinSliderX = QSlider(Qt.Horizontal)
-        axisMinSliderX.setMinimum(0)
-        axisMinSliderX.setTickInterval(1)
-        axisMinSliderX.setEnabled(True)
-        axisMaxSliderX = QSlider(Qt.Horizontal)
-        axisMaxSliderX.setMinimum(1)
-        axisMaxSliderX.setTickInterval(1)
-        axisMaxSliderX.setEnabled(True)
-        axisMinSliderZ = QSlider(Qt.Horizontal)
-        axisMinSliderZ.setMinimum(0)
-        axisMinSliderZ.setTickInterval(1)
-        axisMinSliderZ.setEnabled(True)
-        axisMaxSliderZ = QSlider(Qt.Horizontal)
-        axisMaxSliderZ.setMinimum(1)
-        axisMaxSliderZ.setTickInterval(1)
-        axisMaxSliderZ.setEnabled(True)
-        axisVBox = QVBoxLayout(axisGroupBox)
-        axisVBox.addWidget(QLabel("Column range"))
-        axisVBox.addWidget(axisMinSliderX)
-        axisVBox.addWidget(axisMaxSliderX)
-        axisVBox.addWidget(QLabel("Row range"))
-        axisVBox.addWidget(axisMinSliderZ)
-        axisVBox.addWidget(axisMaxSliderZ)
-        # Mode-dependent controls
-        # sqrt-sin
-        colorGroupBox = QGroupBox("Custom gradient")
-
-        pixmap = gradientBtoYPB_Pixmap()
-        gradientBtoYPB = QPushButton(self._surfaceWidget)
-        gradientBtoYPB.setIcon(QIcon(pixmap))
-        gradientBtoYPB.setIconSize(pixmap.size())
-
-        pixmap = gradientGtoRPB_Pixmap()
-        gradientGtoRPB = QPushButton(self._surfaceWidget)
-        gradientGtoRPB.setIcon(QIcon(pixmap))
-        gradientGtoRPB.setIconSize(pixmap.size())
-
-        colorHBox = QHBoxLayout(colorGroupBox)
-        colorHBox.addWidget(gradientBtoYPB)
-        colorHBox.addWidget(gradientGtoRPB)
-        # Multiseries heightmap
-        showGroupBox = QGroupBox("Show Object")
-        showGroupBox.setVisible(False)
-        checkboxShowOilRigOne = QCheckBox("Oil Rig 1")
-        checkboxShowOilRigOne.setChecked(True)
-        checkboxShowOilRigTwo = QCheckBox("Oil Rig 2")
-        checkboxShowOilRigTwo.setChecked(True)
-        checkboxShowRefinery = QCheckBox("Refinery")
-        showVBox = QVBoxLayout()
-        showVBox.addWidget(checkboxShowOilRigOne)
-        showVBox.addWidget(checkboxShowOilRigTwo)
-        showVBox.addWidget(checkboxShowRefinery)
-        showGroupBox.setLayout(showVBox)
-        visualsGroupBox = QGroupBox("Visuals")
-        visualsGroupBox.setVisible(False)
-        checkboxVisualsSeeThrough = QCheckBox("See-Through")
-        checkboxHighlightOil = QCheckBox("Highlight Oil")
-        checkboxShowShadows = QCheckBox("Shadows")
-        checkboxShowShadows.setChecked(True)
-        visualVBox = QVBoxLayout(visualsGroupBox)
-        visualVBox.addWidget(checkboxVisualsSeeThrough)
-        visualVBox.addWidget(checkboxHighlightOil)
-        visualVBox.addWidget(checkboxShowShadows)
-        labelSelection = QLabel("Selection:")
-        labelSelection.setVisible(False)
-        labelSelectedItem = QLabel("Nothing")
-        labelSelectedItem.setVisible(False)
-        # Textured topography heightmap
-        enableTexture = QCheckBox("Surface texture")
-        enableTexture.setVisible(False)
-
-        label = QLabel(self._surfaceWidget)
-        label.setPixmap(highlightPixmap())
-        heightMapGroupBox = QGroupBox("Highlight color map")
-        colorMapVBox = QVBoxLayout()
-        colorMapVBox.addWidget(label)
-        heightMapGroupBox.setLayout(colorMapVBox)
-        heightMapGroupBox.setVisible(False)
-        # Populate vertical layout
-        # Common
-        vLayout.addWidget(modelGroupBox)
-        vLayout.addWidget(selectionGroupBox)
-        vLayout.addWidget(axisGroupBox)
-        # Sqrt Sin
-        vLayout.addWidget(colorGroupBox)
-        # Multiseries heightmap
-        vLayout.addWidget(showGroupBox)
-        vLayout.addWidget(visualsGroupBox)
-        vLayout.addWidget(labelSelection)
-        vLayout.addWidget(labelSelectedItem)
-        # Textured topography
-        vLayout.addWidget(heightMapGroupBox)
-        vLayout.addWidget(enableTexture)
-        # Create the controller
-        modifier = SurfaceGraphModifier(self._surfaceGraph, labelSelectedItem, self)
-        # Connect widget controls to controller
-        heightMapModelRB.toggled.connect(modifier.enableHeightMapModel)
-        sqrtSinModelRB.toggled.connect(modifier.enableSqrtSinModel)
-        texturedModelRB.toggled.connect(modifier.enableTopographyModel)
-        modeNoneRB.toggled.connect(modifier.toggleModeNone)
-        modeItemRB.toggled.connect(modifier.toggleModeItem)
-        modeSliceRowRB.toggled.connect(modifier.toggleModeSliceRow)
-        modeSliceColumnRB.toggled.connect(modifier.toggleModeSliceColumn)
-        axisMinSliderX.valueChanged.connect(modifier.adjustXMin)
-        axisMaxSliderX.valueChanged.connect(modifier.adjustXMax)
-        axisMinSliderZ.valueChanged.connect(modifier.adjustZMin)
-        axisMaxSliderZ.valueChanged.connect(modifier.adjustZMax)
-        # Mode dependent connections
-        gradientBtoYPB.pressed.connect(modifier.setBlackToYellowGradient)
-        gradientGtoRPB.pressed.connect(modifier.setGreenToRedGradient)
-        checkboxShowOilRigOne.stateChanged.connect(modifier.toggleItemOne)
-        checkboxShowOilRigTwo.stateChanged.connect(modifier.toggleItemTwo)
-        checkboxShowRefinery.stateChanged.connect(modifier.toggleItemThree)
-        checkboxVisualsSeeThrough.stateChanged.connect(modifier.toggleSeeThrough)
-        checkboxHighlightOil.stateChanged.connect(modifier.toggleOilHighlight)
-        checkboxShowShadows.stateChanged.connect(modifier.toggleShadows)
-        enableTexture.stateChanged.connect(modifier.toggleSurfaceTexture)
-        # Connections to disable features depending on mode
-        sqrtSinModelRB.toggled.connect(colorGroupBox.setVisible)
-        heightMapModelRB.toggled.connect(showGroupBox.setVisible)
-        heightMapModelRB.toggled.connect(visualsGroupBox.setVisible)
-        heightMapModelRB.toggled.connect(labelSelection.setVisible)
-        heightMapModelRB.toggled.connect(labelSelectedItem.setVisible)
-        texturedModelRB.toggled.connect(enableTexture.setVisible)
-        texturedModelRB.toggled.connect(heightMapGroupBox.setVisible)
-        modifier.setAxisMinSliderX(axisMinSliderX)
-        modifier.setAxisMaxSliderX(axisMaxSliderX)
-        modifier.setAxisMinSliderZ(axisMinSliderZ)
-        modifier.setAxisMaxSliderZ(axisMaxSliderZ)
-        sqrtSinModelRB.setChecked(True)
-        modeItemRB.setChecked(True)
-        enableTexture.setChecked(True)
-
-    def surfaceWidget(self):
-        return self._surfaceWidget
diff --git a/examples/graphs/3d/widgetgallery/surfacegraphmodifier.py b/examples/graphs/3d/widgetgallery/surfacegraphmodifier.py
deleted file mode 100644 (file)
index b2706c6..0000000
+++ /dev/null
@@ -1,641 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import os
-from math import sqrt, sin
-from pathlib import Path
-
-from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Slot
-from PySide6.QtGui import (QColor, QFont, QImage, QLinearGradient,
-                           QQuaternion, QVector3D)
-from PySide6.QtGraphs import (QAbstract3DGraph, QCustom3DItem,
-                              QCustom3DLabel, QHeightMapSurfaceDataProxy,
-                              QValue3DAxis, QSurfaceDataItem,
-                              QSurfaceDataProxy, QSurface3DSeries,
-                              Q3DInputHandler, Q3DTheme)
-
-
-from highlightseries import HighlightSeries
-from topographicseries import TopographicSeries
-from custominputhandler import CustomInputHandler
-
-
-SAMPLE_COUNT_X = 150
-SAMPLE_COUNT_Z = 150
-HEIGHTMAP_GRID_STEP_X = 6
-HEIGHTMAP_GRID_STEP_Z = 6
-SAMPLE_MIN = -8.0
-SAMPLE_MAX = 8.0
-
-AREA_WIDTH = 8000.0
-AREA_HEIGHT = 8000.0
-ASPECT_RATIO = 0.1389
-MIN_RANGE = AREA_WIDTH * 0.49
-
-
-class SurfaceGraphModifier(QObject):
-
-    def __init__(self, surface, label, parent):
-        super().__init__(parent)
-        self._data_path = Path(__file__).resolve().parent / "data"
-        self._graph = surface
-        self._textField = label
-        self._sqrtSinProxy = None
-        self._sqrtSinSeries = None
-        self._heightMapProxyOne = None
-        self._heightMapProxyTwo = None
-        self._heightMapProxyThree = None
-        self._heightMapSeriesOne = None
-        self._heightMapSeriesTwo = None
-        self._heightMapSeriesThree = None
-
-        self._axisMinSliderX = None
-        self._axisMaxSliderX = None
-        self._axisMinSliderZ = None
-        self._axisMaxSliderZ = None
-        self._rangeMinX = 0.0
-        self._rangeMinZ = 0.0
-        self._stepX = 0.0
-        self._stepZ = 0.0
-        self._heightMapWidth = 0
-        self._heightMapHeight = 0
-
-        self._selectionAnimation = None
-        self._titleLabel = None
-        self._previouslyAnimatedItem = None
-        self._previousScaling = {}
-
-        self._topography = None
-        self._highlight = None
-        self._highlightWidth = 0
-        self._highlightHeight = 0
-
-        self._customInputHandler = None
-        self._defaultInputHandler = Q3DInputHandler()
-
-        self._graph.setCameraZoomLevel(85.0)
-        self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset.IsometricRight)
-        self._graph.activeTheme().setType(Q3DTheme.Theme.Retro)
-
-        self._x_axis = QValue3DAxis()
-        self._y_axis = QValue3DAxis()
-        self._z_axis = QValue3DAxis()
-        self._graph.setAxisX(self._x_axis)
-        self._graph.setAxisY(self._y_axis)
-        self._graph.setAxisZ(self._z_axis)
-
-        #
-        # Sqrt Sin
-        #
-        self._sqrtSinProxy = QSurfaceDataProxy()
-        self._sqrtSinSeries = QSurface3DSeries(self._sqrtSinProxy)
-        self.fillSqrtSinProxy()
-
-        #
-        # Multisurface heightmap
-        #
-        # Create the first surface layer
-        heightMapImageOne = QImage(self._data_path / "layer_1.png")
-        self._heightMapProxyOne = QHeightMapSurfaceDataProxy(heightMapImageOne)
-        self._heightMapSeriesOne = QSurface3DSeries(self._heightMapProxyOne)
-        self._heightMapSeriesOne.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
-        self._heightMapProxyOne.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
-        # Create the other 2 surface layers
-        heightMapImageTwo = QImage(self._data_path / "layer_2.png")
-        self._heightMapProxyTwo = QHeightMapSurfaceDataProxy(heightMapImageTwo)
-        self._heightMapSeriesTwo = QSurface3DSeries(self._heightMapProxyTwo)
-        self._heightMapSeriesTwo.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
-        self._heightMapProxyTwo.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
-        heightMapImageThree = QImage(self._data_path / "layer_3.png")
-        self._heightMapProxyThree = QHeightMapSurfaceDataProxy(heightMapImageThree)
-        self._heightMapSeriesThree = QSurface3DSeries(self._heightMapProxyThree)
-        self._heightMapSeriesThree.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
-        self._heightMapProxyThree.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
-        # The images are the same size, so it's enough to get the dimensions
-        # from one
-        self._heightMapWidth = heightMapImageOne.width()
-        self._heightMapHeight = heightMapImageOne.height()
-
-        # Set the gradients for multi-surface layers
-        grOne = QLinearGradient()
-        grOne.setColorAt(0.0, Qt.black)
-        grOne.setColorAt(0.38, Qt.darkYellow)
-        grOne.setColorAt(0.39, Qt.darkGreen)
-        grOne.setColorAt(0.5, Qt.darkGray)
-        grOne.setColorAt(1.0, Qt.gray)
-        self._heightMapSeriesOne.setBaseGradient(grOne)
-        self._heightMapSeriesOne.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
-        grTwo = QLinearGradient()
-        grTwo.setColorAt(0.39, Qt.blue)
-        grTwo.setColorAt(0.4, Qt.white)
-        self._heightMapSeriesTwo.setBaseGradient(grTwo)
-        self._heightMapSeriesTwo.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
-        grThree = QLinearGradient()
-        grThree.setColorAt(0.0, Qt.white)
-        grThree.setColorAt(0.05, Qt.black)
-        self._heightMapSeriesThree.setBaseGradient(grThree)
-        self._heightMapSeriesThree.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
-        # Custom items and label
-        self._graph.selectedElementChanged.connect(self.handleElementSelected)
-
-        self._selectionAnimation = QPropertyAnimation(self)
-        self._selectionAnimation.setPropertyName(b"scaling")
-        self._selectionAnimation.setDuration(500)
-        self._selectionAnimation.setLoopCount(-1)
-
-        titleFont = QFont("Century Gothic", 30)
-        titleFont.setBold(True)
-        self._titleLabel = QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
-                                          QVector3D(0.0, 1.2, 0.0),
-                                          QVector3D(1.0, 1.0, 0.0),
-                                          QQuaternion())
-        self._titleLabel.setPositionAbsolute(True)
-        self._titleLabel.setFacingCamera(True)
-        self._titleLabel.setBackgroundColor(QColor(0x66cdaa))
-        self._graph.addCustomItem(self._titleLabel)
-        self._titleLabel.setVisible(False)
-
-        # Make two of the custom object visible
-        self.toggleItemOne(True)
-        self.toggleItemTwo(True)
-
-        #
-        # Topographic map
-        #
-        self._topography = TopographicSeries()
-        file_name = os.fspath(self._data_path / "topography.png")
-        self._topography.setTopographyFile(file_name, AREA_WIDTH, AREA_HEIGHT)
-        self._topography.setItemLabelFormat("@yLabel m")
-
-        self._highlight = HighlightSeries()
-        self._highlight.setTopographicSeries(self._topography)
-        self._highlight.setMinHeight(MIN_RANGE * ASPECT_RATIO)
-        self._highlight.handleGradientChange(AREA_WIDTH * ASPECT_RATIO)
-        self._graph.axisY().maxChanged.connect(self._highlight.handleGradientChange)
-
-        self._customInputHandler = CustomInputHandler(self._graph)
-        self._customInputHandler.setHighlightSeries(self._highlight)
-        self._customInputHandler.setAxes(self._x_axis, self._y_axis, self._z_axis)
-        self._customInputHandler.setLimits(0.0, AREA_WIDTH, MIN_RANGE)
-        self._customInputHandler.setAspectRatio(ASPECT_RATIO)
-
-    def fillSqrtSinProxy(self):
-        stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
-        stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
-
-        dataArray = []
-        for i in range(0, SAMPLE_COUNT_Z):
-            newRow = []
-            # Keep values within range bounds, since just adding step can
-            # cause minor drift due to the rounding errors.
-            z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
-            for j in range(0, SAMPLE_COUNT_X):
-                x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
-                R = sqrt(z * z + x * x) + 0.01
-                y = (sin(R) / R + 0.24) * 1.61
-                item = QSurfaceDataItem(QVector3D(x, y, z))
-                newRow.append(item)
-            dataArray.append(newRow)
-        self._sqrtSinProxy.resetArray(dataArray)
-
-    @Slot(bool)
-    def enableSqrtSinModel(self, enable):
-        if enable:
-            self._sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawSurfaceAndWireframe)
-            self._sqrtSinSeries.setFlatShadingEnabled(True)
-
-            self._graph.axisX().setLabelFormat("%.2f")
-            self._graph.axisZ().setLabelFormat("%.2f")
-            self._graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
-            self._graph.axisY().setRange(0.0, 2.0)
-            self._graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
-            self._graph.axisX().setLabelAutoRotation(30.0)
-            self._graph.axisY().setLabelAutoRotation(90.0)
-            self._graph.axisZ().setLabelAutoRotation(30.0)
-
-            self._graph.removeSeries(self._heightMapSeriesOne)
-            self._graph.removeSeries(self._heightMapSeriesTwo)
-            self._graph.removeSeries(self._heightMapSeriesThree)
-            self._graph.removeSeries(self._topography)
-            self._graph.removeSeries(self._highlight)
-
-            self._graph.addSeries(self._sqrtSinSeries)
-
-            self._titleLabel.setVisible(False)
-            self._graph.axisX().setTitleVisible(False)
-            self._graph.axisY().setTitleVisible(False)
-            self._graph.axisZ().setTitleVisible(False)
-
-            self._graph.axisX().setTitle("")
-            self._graph.axisY().setTitle("")
-            self._graph.axisZ().setTitle("")
-
-            self._graph.setActiveInputHandler(self._defaultInputHandler)
-
-            # Reset range sliders for Sqrt & Sin
-            self._rangeMinX = SAMPLE_MIN
-            self._rangeMinZ = SAMPLE_MIN
-            self._stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
-            self._stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
-            self._axisMinSliderX.setMinimum(0)
-            self._axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
-            self._axisMinSliderX.setValue(0)
-            self._axisMaxSliderX.setMinimum(1)
-            self._axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
-            self._axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
-            self._axisMinSliderZ.setMinimum(0)
-            self._axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
-            self._axisMinSliderZ.setValue(0)
-            self._axisMaxSliderZ.setMinimum(1)
-            self._axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
-            self._axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
-
-    @Slot(bool)
-    def enableHeightMapModel(self, enable):
-        if enable:
-            self._heightMapSeriesOne.setDrawMode(QSurface3DSeries.DrawSurface)
-            self._heightMapSeriesOne.setFlatShadingEnabled(False)
-            self._heightMapSeriesTwo.setDrawMode(QSurface3DSeries.DrawSurface)
-            self._heightMapSeriesTwo.setFlatShadingEnabled(False)
-            self._heightMapSeriesThree.setDrawMode(QSurface3DSeries.DrawSurface)
-            self._heightMapSeriesThree.setFlatShadingEnabled(False)
-
-            self._graph.axisX().setLabelFormat("%.1f N")
-            self._graph.axisZ().setLabelFormat("%.1f E")
-            self._graph.axisX().setRange(34.0, 40.0)
-            self._graph.axisY().setAutoAdjustRange(True)
-            self._graph.axisZ().setRange(18.0, 24.0)
-
-            self._graph.axisX().setTitle("Latitude")
-            self._graph.axisY().setTitle("Height")
-            self._graph.axisZ().setTitle("Longitude")
-
-            self._graph.removeSeries(self._sqrtSinSeries)
-            self._graph.removeSeries(self._topography)
-            self._graph.removeSeries(self._highlight)
-            self._graph.addSeries(self._heightMapSeriesOne)
-            self._graph.addSeries(self._heightMapSeriesTwo)
-            self._graph.addSeries(self._heightMapSeriesThree)
-
-            self._graph.setActiveInputHandler(self._defaultInputHandler)
-
-            self._titleLabel.setVisible(True)
-            self._graph.axisX().setTitleVisible(True)
-            self._graph.axisY().setTitleVisible(True)
-            self._graph.axisZ().setTitleVisible(True)
-
-            # Reset range sliders for height map
-            mapGridCountX = self._heightMapWidth / HEIGHTMAP_GRID_STEP_X
-            mapGridCountZ = self._heightMapHeight / HEIGHTMAP_GRID_STEP_Z
-            self._rangeMinX = 34.0
-            self._rangeMinZ = 18.0
-            self._stepX = 6.0 / float(mapGridCountX - 1)
-            self._stepZ = 6.0 / float(mapGridCountZ - 1)
-            self._axisMinSliderX.setMinimum(0)
-            self._axisMinSliderX.setMaximum(mapGridCountX - 2)
-            self._axisMinSliderX.setValue(0)
-            self._axisMaxSliderX.setMinimum(1)
-            self._axisMaxSliderX.setMaximum(mapGridCountX - 1)
-            self._axisMaxSliderX.setValue(mapGridCountX - 1)
-            self._axisMinSliderZ.setMinimum(0)
-            self._axisMinSliderZ.setMaximum(mapGridCountZ - 2)
-            self._axisMinSliderZ.setValue(0)
-            self._axisMaxSliderZ.setMinimum(1)
-            self._axisMaxSliderZ.setMaximum(mapGridCountZ - 1)
-            self._axisMaxSliderZ.setValue(mapGridCountZ - 1)
-
-    @Slot(bool)
-    def enableTopographyModel(self, enable):
-        if enable:
-            self._graph.axisX().setLabelFormat("%i")
-            self._graph.axisZ().setLabelFormat("%i")
-            self._graph.axisX().setRange(0.0, AREA_WIDTH)
-            self._graph.axisY().setRange(100.0, AREA_WIDTH * ASPECT_RATIO)
-            self._graph.axisZ().setRange(0.0, AREA_HEIGHT)
-            self._graph.axisX().setLabelAutoRotation(30.0)
-            self._graph.axisY().setLabelAutoRotation(90.0)
-            self._graph.axisZ().setLabelAutoRotation(30.0)
-
-            self._graph.removeSeries(self._heightMapSeriesOne)
-            self._graph.removeSeries(self._heightMapSeriesTwo)
-            self._graph.removeSeries(self._heightMapSeriesThree)
-            self._graph.addSeries(self._topography)
-            self._graph.addSeries(self._highlight)
-
-            self._titleLabel.setVisible(False)
-            self._graph.axisX().setTitleVisible(False)
-            self._graph.axisY().setTitleVisible(False)
-            self._graph.axisZ().setTitleVisible(False)
-
-            self._graph.axisX().setTitle("")
-            self._graph.axisY().setTitle("")
-            self._graph.axisZ().setTitle("")
-
-            self._graph.setActiveInputHandler(self._customInputHandler)
-
-            # Reset range sliders for topography map
-            self._rangeMinX = 0.0
-            self._rangeMinZ = 0.0
-            self._stepX = 1.0
-            self._stepZ = 1.0
-            self._axisMinSliderX.setMinimum(0)
-            self._axisMinSliderX.setMaximum(AREA_WIDTH - 200)
-            self._axisMinSliderX.setValue(0)
-            self._axisMaxSliderX.setMinimum(200)
-            self._axisMaxSliderX.setMaximum(AREA_WIDTH)
-            self._axisMaxSliderX.setValue(AREA_WIDTH)
-            self._axisMinSliderZ.setMinimum(0)
-            self._axisMinSliderZ.setMaximum(AREA_HEIGHT - 200)
-            self._axisMinSliderZ.setValue(0)
-            self._axisMaxSliderZ.setMinimum(200)
-            self._axisMaxSliderZ.setMaximum(AREA_HEIGHT)
-            self._axisMaxSliderZ.setValue(AREA_HEIGHT)
-
-    def adjustXMin(self, min):
-        minX = self._stepX * float(min) + self._rangeMinX
-
-        max = self._axisMaxSliderX.value()
-        if min >= max:
-            max = min + 1
-            self._axisMaxSliderX.setValue(max)
-
-        maxX = self._stepX * max + self._rangeMinX
-
-        self.setAxisXRange(minX, maxX)
-
-    def adjustXMax(self, max):
-        maxX = self._stepX * float(max) + self._rangeMinX
-
-        min = self._axisMinSliderX.value()
-        if max <= min:
-            min = max - 1
-            self._axisMinSliderX.setValue(min)
-
-        minX = self._stepX * min + self._rangeMinX
-
-        self.setAxisXRange(minX, maxX)
-
-    def adjustZMin(self, min):
-        minZ = self._stepZ * float(min) + self._rangeMinZ
-
-        max = self._axisMaxSliderZ.value()
-        if min >= max:
-            max = min + 1
-            self._axisMaxSliderZ.setValue(max)
-
-        maxZ = self._stepZ * max + self._rangeMinZ
-
-        self.setAxisZRange(minZ, maxZ)
-
-    def adjustZMax(self, max):
-        maxX = self._stepZ * float(max) + self._rangeMinZ
-
-        min = self._axisMinSliderZ.value()
-        if max <= min:
-            min = max - 1
-            self._axisMinSliderZ.setValue(min)
-
-        minX = self._stepZ * min + self._rangeMinZ
-
-        self.setAxisZRange(minX, maxX)
-
-    def setAxisXRange(self, min, max):
-        self._graph.axisX().setRange(min, max)
-
-    def setAxisZRange(self, min, max):
-        self._graph.axisZ().setRange(min, max)
-
-    def setBlackToYellowGradient(self):
-        gr = QLinearGradient()
-        gr.setColorAt(0.0, Qt.black)
-        gr.setColorAt(0.33, Qt.blue)
-        gr.setColorAt(0.67, Qt.red)
-        gr.setColorAt(1.0, Qt.yellow)
-
-        self._sqrtSinSeries.setBaseGradient(gr)
-        self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
-    def setGreenToRedGradient(self):
-        gr = QLinearGradient()
-        gr.setColorAt(0.0, Qt.darkGreen)
-        gr.setColorAt(0.5, Qt.yellow)
-        gr.setColorAt(0.8, Qt.red)
-        gr.setColorAt(1.0, Qt.darkRed)
-
-        self._sqrtSinSeries.setBaseGradient(gr)
-        self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
-    @Slot(bool)
-    def toggleItemOne(self, show):
-        positionOne = QVector3D(39.0, 77.0, 19.2)
-        positionOnePipe = QVector3D(39.0, 45.0, 19.2)
-        positionOneLabel = QVector3D(39.0, 107.0, 19.2)
-        if show:
-            color = QImage(2, 2, QImage.Format_RGB32)
-            color.fill(Qt.red)
-            file_name = os.fspath(self._data_path / "oilrig.mesh")
-            item = QCustom3DItem(file_name, positionOne,
-                                 QVector3D(0.025, 0.025, 0.025),
-                                 QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 45.0),
-                                 color)
-            self._graph.addCustomItem(item)
-            file_name = os.fspath(self._data_path / "pipe.mesh")
-            item = QCustom3DItem(file_name, positionOnePipe,
-                                 QVector3D(0.005, 0.5, 0.005), QQuaternion(),
-                                 color)
-            item.setShadowCasting(False)
-            self._graph.addCustomItem(item)
-
-            label = QCustom3DLabel()
-            label.setText("Oil Rig One")
-            label.setPosition(positionOneLabel)
-            label.setScaling(QVector3D(1.0, 1.0, 1.0))
-            self._graph.addCustomItem(label)
-        else:
-            self.resetSelection()
-            self._graph.removeCustomItemAt(positionOne)
-            self._graph.removeCustomItemAt(positionOnePipe)
-            self._graph.removeCustomItemAt(positionOneLabel)
-
-    @Slot(bool)
-    def toggleItemTwo(self, show):
-        positionTwo = QVector3D(34.5, 77.0, 23.4)
-        positionTwoPipe = QVector3D(34.5, 45.0, 23.4)
-        positionTwoLabel = QVector3D(34.5, 107.0, 23.4)
-        if show:
-            color = QImage(2, 2, QImage.Format_RGB32)
-            color.fill(Qt.red)
-            item = QCustom3DItem()
-            file_name = os.fspath(self._data_path / "oilrig.mesh")
-            item.setMeshFile(file_name)
-            item.setPosition(positionTwo)
-            item.setScaling(QVector3D(0.025, 0.025, 0.025))
-            item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 25.0))
-            item.setTextureImage(color)
-            self._graph.addCustomItem(item)
-            file_name = os.fspath(self._data_path / "pipe.mesh")
-            item = QCustom3DItem(file_name, positionTwoPipe,
-                                 QVector3D(0.005, 0.5, 0.005), QQuaternion(),
-                                 color)
-            item.setShadowCasting(False)
-            self._graph.addCustomItem(item)
-
-            label = QCustom3DLabel()
-            label.setText("Oil Rig Two")
-            label.setPosition(positionTwoLabel)
-            label.setScaling(QVector3D(1.0, 1.0, 1.0))
-            self._graph.addCustomItem(label)
-        else:
-            self.resetSelection()
-            self._graph.removeCustomItemAt(positionTwo)
-            self._graph.removeCustomItemAt(positionTwoPipe)
-            self._graph.removeCustomItemAt(positionTwoLabel)
-
-    @Slot(bool)
-    def toggleItemThree(self, show):
-        positionThree = QVector3D(34.5, 86.0, 19.1)
-        positionThreeLabel = QVector3D(34.5, 116.0, 19.1)
-        if show:
-            color = QImage(2, 2, QImage.Format_RGB32)
-            color.fill(Qt.darkMagenta)
-            item = QCustom3DItem()
-            file_name = os.fspath(self._data_path / "refinery.mesh")
-            item.setMeshFile(file_name)
-            item.setPosition(positionThree)
-            item.setScaling(QVector3D(0.04, 0.04, 0.04))
-            item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 75.0))
-            item.setTextureImage(color)
-            self._graph.addCustomItem(item)
-
-            label = QCustom3DLabel()
-            label.setText("Refinery")
-            label.setPosition(positionThreeLabel)
-            label.setScaling(QVector3D(1.0, 1.0, 1.0))
-            self._graph.addCustomItem(label)
-        else:
-            self.resetSelection()
-            self._graph.removeCustomItemAt(positionThree)
-            self._graph.removeCustomItemAt(positionThreeLabel)
-
-    @Slot(bool)
-    def toggleSeeThrough(self, seethrough):
-        s0 = self._graph.seriesList()[0]
-        s1 = self._graph.seriesList()[1]
-        if seethrough:
-            s0.setDrawMode(QSurface3DSeries.DrawWireframe)
-            s1.setDrawMode(QSurface3DSeries.DrawWireframe)
-        else:
-            s0.setDrawMode(QSurface3DSeries.DrawSurface)
-            s1.setDrawMode(QSurface3DSeries.DrawSurface)
-
-    @Slot(bool)
-    def toggleOilHighlight(self, highlight):
-        s2 = self._graph.seriesList()[2]
-        if highlight:
-            grThree = QLinearGradient()
-            grThree.setColorAt(0.0, Qt.black)
-            grThree.setColorAt(0.05, Qt.red)
-            s2.setBaseGradient(grThree)
-        else:
-            grThree = QLinearGradient()
-            grThree.setColorAt(0.0, Qt.white)
-            grThree.setColorAt(0.05, Qt.black)
-            s2.setBaseGradient(grThree)
-
-    @Slot(bool)
-    def toggleShadows(self, shadows):
-        sq = (QAbstract3DGraph.ShadowQualityMedium
-              if shadows else QAbstract3DGraph.ShadowQualityNone)
-        self._graph.setShadowQuality(sq)
-
-    @Slot(bool)
-    def toggleSurfaceTexture(self, enable):
-        if enable:
-            file_name = os.fspath(self._data_path / "maptexture.jpg")
-            self._topography.setTextureFile(file_name)
-        else:
-            self._topography.setTextureFile("")
-
-    def handleElementSelected(self, type):
-        self.resetSelection()
-        if type == QAbstract3DGraph.ElementCustomItem:
-            item = self._graph.selectedCustomItem()
-            text = ""
-            if isinstance(item, QCustom3DItem):
-                text += "Custom label: "
-            else:
-                file = item.meshFile().split("/")[-1]
-                text += f"{file}: "
-
-            text += str(self._graph.selectedCustomItemIndex())
-            self._textField.setText(text)
-            self._previouslyAnimatedItem = item
-            self._previousScaling = item.scaling()
-            self._selectionAnimation.setTargetObject(item)
-            self._selectionAnimation.setStartValue(item.scaling())
-            self._selectionAnimation.setEndValue(item.scaling() * 1.5)
-            self._selectionAnimation.start()
-        elif type == QAbstract3DGraph.ElementSeries:
-            text = "Surface ("
-            series = self._graph.selectedSeries()
-            if series:
-                point = series.selectedPoint()
-                text += f"{point.x()}, {point.y()}"
-            text += ")"
-            self._textField.setText(text)
-        elif (type.value > QAbstract3DGraph.ElementSeries.value
-              and type < QAbstract3DGraph.ElementCustomItem.value):
-            index = self._graph.selectedLabelIndex()
-            text = ""
-            if type == QAbstract3DGraph.ElementAxisXLabel:
-                text += "Axis X label: "
-            elif type == QAbstract3DGraph.ElementAxisYLabel:
-                text += "Axis Y label: "
-            else:
-                text += "Axis Z label: "
-            text += str(index)
-            self._textField.setText(text)
-        else:
-            self._textField.setText("Nothing")
-
-    def resetSelection(self):
-        self._selectionAnimation.stop()
-        if self._previouslyAnimatedItem:
-            self._previouslyAnimatedItem.setScaling(self._previousScaling)
-        self._previouslyAnimatedItem = None
-
-    def toggleModeNone(self):
-        self._graph.setSelectionMode(QAbstract3DGraph.SelectionNone)
-
-    def toggleModeItem(self):
-        self._graph.setSelectionMode(QAbstract3DGraph.SelectionItem)
-
-    def toggleModeSliceRow(self):
-        sm = (QAbstract3DGraph.SelectionItemAndRow
-              | QAbstract3DGraph.SelectionSlice
-              | QAbstract3DGraph.SelectionMultiSeries)
-        self._graph.setSelectionMode(sm)
-
-    def toggleModeSliceColumn(self):
-        sm = (QAbstract3DGraph.SelectionItemAndColumn
-              | QAbstract3DGraph.SelectionSlice
-              | QAbstract3DGraph.SelectionMultiSeries)
-        self._graph.setSelectionMode(sm)
-
-    def setAxisMinSliderX(self, slider):
-        self._axisMinSliderX = slider
-
-    def setAxisMaxSliderX(self, slider):
-        self._axisMaxSliderX = slider
-
-    def setAxisMinSliderZ(self, slider):
-        self._axisMinSliderZ = slider
-
-    def setAxisMaxSliderZ(self, slider):
-        self._axisMaxSliderZ = slider
diff --git a/examples/graphs/3d/widgetgallery/topographicseries.py b/examples/graphs/3d/widgetgallery/topographicseries.py
deleted file mode 100644 (file)
index 4f286a2..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QImage, QVector3D
-from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem)
-
-
-# Value used to encode height data as RGB value on PNG file
-PACKING_FACTOR = 11983.0
-
-
-class TopographicSeries(QSurface3DSeries):
-
-    def __init__(self):
-        super().__init__()
-        self._sampleCountX = 0.0
-        self._sampleCountZ = 0.0
-        self.setDrawMode(QSurface3DSeries.DrawSurface)
-        self.setFlatShadingEnabled(True)
-        self.setBaseColor(Qt.white)
-
-    def sampleCountX(self):
-        return self._sampleCountX
-
-    def sampleCountZ(self):
-        return self._sampleCountZ
-
-    def setTopographyFile(self, file, width, height):
-        heightMapImage = QImage(file)
-        bits = heightMapImage.bits()
-        imageHeight = heightMapImage.height()
-        imageWidth = heightMapImage.width()
-        widthBits = imageWidth * 4
-        stepX = width / float(imageWidth)
-        stepZ = height / float(imageHeight)
-
-        dataArray = []
-        for i in range(0, imageHeight):
-            p = i * widthBits
-            z = height - float(i) * stepZ
-            newRow = []
-            for j in range(0, imageWidth):
-                aa = bits[p + 0]
-                rr = bits[p + 1]
-                gg = bits[p + 2]
-                color = (gg << 16) + (rr << 8) + aa
-                y = float(color) / PACKING_FACTOR
-                item = QSurfaceDataItem(QVector3D(float(j) * stepX, y, z))
-                newRow.append(item)
-                p += 4
-            dataArray.append(newRow)
-
-        self.dataProxy().resetArray(dataArray)
-
-        self._sampleCountX = float(imageWidth)
-        self._sampleCountZ = float(imageHeight)
diff --git a/examples/graphs/3d/widgetgallery/variantbardatamapping.py b/examples/graphs/3d/widgetgallery/variantbardatamapping.py
deleted file mode 100644 (file)
index 50bdefa..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, Signal
-
-
-class VariantBarDataMapping(QObject):
-
-    rowIndexChanged = Signal()
-    columnIndexChanged = Signal()
-    valueIndexChanged = Signal()
-    rowCategoriesChanged = Signal()
-    columnCategoriesChanged = Signal()
-    mappingChanged = Signal()
-
-    def __init__(self, rowIndex, columnIndex, valueIndex,
-                 rowCategories=[], columnCategories=[]):
-        super().__init__(None)
-        self._rowIndex = rowIndex
-        self._columnIndex = columnIndex
-        self._valueIndex = valueIndex
-        self._rowCategories = rowCategories
-        self._columnCategories = columnCategories
-
-    def setRowIndex(self, index):
-        self._rowIndex = index
-        self.mappingChanged.emit()
-
-    def rowIndex(self):
-        return self._rowIndex
-
-    def setColumnIndex(self, index):
-        self._columnIndex = index
-        self.mappingChanged.emit()
-
-    def columnIndex(self):
-        return self._columnIndex
-
-    def setValueIndex(self, index):
-        self._valueIndex = index
-        self.mappingChanged.emit()
-
-    def valueIndex(self):
-        return self._valueIndex
-
-    def setRowCategories(self, categories):
-        self._rowCategories = categories
-        self.mappingChanged.emit()
-
-    def rowCategories(self):
-        return self._rowCategories
-
-    def setColumnCategories(self, categories):
-        self._columnCategories = categories
-        self.mappingChanged.emit()
-
-    def columnCategories(self):
-        return self._columnCategories
-
-    def remap(self, rowIndex, columnIndex, valueIndex,
-              rowCategories=[], columnCategories=[]):
-        self._rowIndex = rowIndex
-        self._columnIndex = columnIndex
-        self._valueIndex = valueIndex
-        self._rowCategories = rowCategories
-        self._columnCategories = columnCategories
-        self.mappingChanged.emit()
diff --git a/examples/graphs/3d/widgetgallery/variantbardataproxy.py b/examples/graphs/3d/widgetgallery/variantbardataproxy.py
deleted file mode 100644 (file)
index 5ab2a2c..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import Slot
-from PySide6.QtGraphs import QBarDataProxy, QBarDataItem
-
-
-class VariantBarDataProxy(QBarDataProxy):
-
-    def __init__(self):
-        super().__init__()
-        self._dataSet = None
-        self._mapping = None
-
-    def setDataSet(self, newSet):
-        if self._dataSet:
-            self._dataSet.itemsAdded.disconnect(self.handleItemsAdded)
-            self._dataSet.dataCleared.disconnect(self.handleDataCleared)
-
-        self._dataSet = newSet
-
-        if self._dataSet:
-            self._dataSet.itemsAdded.connect(self.handleItemsAdded)
-            self._dataSet.dataCleared.connect(self.handleDataCleared)
-        self.resolveDataSet()
-
-    def dataSet(self):
-        return self._dataSet.data()
-
-    # Map key (row, column, value) to value index in data item (VariantItem).
-    # Doesn't gain ownership of mapping, but does connect to it to listen for
-    # mapping changes. Modifying mapping that is set to proxy will trigger
-    # dataset re-resolving.
-    def setMapping(self, mapping):
-        if self._mapping:
-            self._mapping.mappingChanged.disconnect(self.handleMappingChanged)
-
-        self._mapping = mapping
-
-        if self._mapping:
-            self._mapping.mappingChanged.connect(self.handleMappingChanged)
-
-        self.resolveDataSet()
-
-    def mapping(self):
-        return self._mapping.data()
-
-    @Slot(int, int)
-    def handleItemsAdded(self, index, count):
-        # Resolve new items
-        self.resolveDataSet()
-
-    @Slot()
-    def handleDataCleared(self):
-        # Data cleared, reset array
-        self.resetArray(None)
-
-    @Slot()
-    def handleMappingChanged(self):
-        self.resolveDataSet()
-
-    # Resolve entire dataset into QBarDataArray.
-    def resolveDataSet(self):
-        # If we have no data or mapping, or the categories are not defined,
-        # simply clear the array
-        if (not self._dataSet or not self._mapping
-                or not self._mapping.rowCategories()
-                or not self._mapping.columnCategories()):
-            self.resetArray()
-            return
-
-        itemList = self._dataSet.itemList()
-
-        rowIndex = self._mapping.rowIndex()
-        columnIndex = self._mapping.columnIndex()
-        valueIndex = self._mapping.valueIndex()
-        rowList = self._mapping.rowCategories()
-        columnList = self._mapping.columnCategories()
-
-        # Sort values into rows and columns
-        itemValueMap = {}
-        for item in itemList:
-            key = str(item[rowIndex])
-            v = itemValueMap.get(key)
-            if not v:
-                v = {}
-                itemValueMap[key] = v
-            v[str(item[columnIndex])] = float(item[valueIndex])
-
-        # Create a new data array in format the parent class understands
-        newProxyArray = []
-        for rowKey in rowList:
-            newProxyRow = []
-            for i in range(0, len(columnList)):
-                item = QBarDataItem(itemValueMap[rowKey][columnList[i]])
-                newProxyRow.append(item)
-            newProxyArray.append(newProxyRow)
-
-        # Finally, reset the data array in the parent class
-        self.resetArray(newProxyArray)
diff --git a/examples/graphs/3d/widgetgallery/variantdataset.py b/examples/graphs/3d/widgetgallery/variantdataset.py
deleted file mode 100644 (file)
index 752bc38..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, Signal
-
-
-class VariantDataSet(QObject):
-
-    itemsAdded = Signal(int, int)
-    dataCleared = Signal()
-
-    def __init__(self):
-        super().__init__()
-        self._variantData = []
-
-    def clear(self):
-        for item in self._variantData:
-            item.clear()
-            del item
-
-        self._variantData.clear()
-        self.dataCleared.emit()
-
-    def addItem(self, item):
-        self._variantData.append(item)
-        addIndex = len(self._variantData)
-
-        self.itemsAdded.emit(addIndex, 1)
-        return addIndex
-
-    def addItems(self, itemList):
-        newCount = len(itemList)
-        addIndex = len(self._variantData)
-        self._variantData.extend(itemList)
-        self.itemsAdded.emit(addIndex, newCount)
-        return addIndex
-
-    def itemList(self):
-        return self._variantData
diff --git a/examples/graphs/3d/widgetgallery/widgetgallery.pyproject b/examples/graphs/3d/widgetgallery/widgetgallery.pyproject
deleted file mode 100644 (file)
index 581b214..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-    "files": ["main.py",
-              "axesinputhandler.py",
-              "bargraph.py",
-              "custominputhandler.py",
-              "graphmodifier.py",
-              "highlightseries.py",
-              "rainfalldata.py",
-              "scatterdatamodifier.py",
-              "scattergraph.py",
-              "surfacegraph.py",
-              "surfacegraphmodifier.py",
-              "topographicseries.py",
-              "variantbardatamapping.py",
-              "variantbardataproxy.py",
-              "variantdataset.py",
-              "data/layer_1.png",
-              "data/layer_2.png",
-              "data/layer_3.png",
-              "data/license.txt",
-              "data/maptexture.jpg",
-              "data/narrowarrow.mesh",
-              "data/oilrig.mesh",
-              "data/pipe.mesh",
-              "data/raindata.txt",
-              "data/refinery.mesh",
-              "data/topography.png"
-]
-}
diff --git a/examples/graphs/3d/widgetgraphgallery/bargraph.py b/examples/graphs/3d/widgetgraphgallery/bargraph.py
new file mode 100644 (file)
index 0000000..3259e8a
--- /dev/null
@@ -0,0 +1,271 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from graphmodifier import GraphModifier
+
+from PySide6.QtCore import QObject, Qt
+from PySide6.QtGui import QFont
+from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
+                               QLabel, QPushButton, QHBoxLayout, QSizePolicy,
+                               QRadioButton, QSlider, QVBoxLayout, QWidget)
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import QAbstract3DSeries, QtGraphs3D
+from PySide6.QtGraphsWidgets import Q3DBarsWidgetItem
+
+
+class BarGraph(QObject):
+
+    def __init__(self, minimum_graph_size, maximum_graph_size):
+        super().__init__()
+
+        barsGraph = Q3DBarsWidgetItem()
+        barsGraphWidget = QQuickWidget()
+        barsGraph.setWidget(barsGraphWidget)
+        self._barsWidget = QWidget()
+        hLayout = QHBoxLayout(self._barsWidget)
+        barsGraphWidget.setMinimumSize(minimum_graph_size)
+        barsGraphWidget.setMaximumSize(maximum_graph_size)
+        barsGraphWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+        barsGraphWidget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+        hLayout.addWidget(barsGraphWidget, 1)
+
+        vLayout = QVBoxLayout()
+        hLayout.addLayout(vLayout)
+
+        themeList = QComboBox(self._barsWidget)
+        themeList.addItem("QtGreen")
+        themeList.addItem("QtGreenNeon")
+        themeList.addItem("MixSeries")
+        themeList.addItem("OrangeSeries")
+        themeList.addItem("YellowSeries")
+        themeList.addItem("BlueSeries")
+        themeList.addItem("PurpleSeries")
+        themeList.addItem("GreySeries")
+        themeList.setCurrentIndex(0)
+
+        labelButton = QPushButton(self._barsWidget)
+        labelButton.setText("Change label style")
+
+        smoothCheckBox = QCheckBox(self._barsWidget)
+        smoothCheckBox.setText("Smooth bars")
+        smoothCheckBox.setChecked(False)
+
+        barStyleList = QComboBox(self._barsWidget)
+        barStyleList.addItem("Bar", QAbstract3DSeries.Mesh.Bar)
+        barStyleList.addItem("Pyramid", QAbstract3DSeries.Mesh.Pyramid)
+        barStyleList.addItem("Cone", QAbstract3DSeries.Mesh.Cone)
+        barStyleList.addItem("Cylinder", QAbstract3DSeries.Mesh.Cylinder)
+        barStyleList.addItem("Bevel bar", QAbstract3DSeries.Mesh.BevelBar)
+        barStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
+        barStyleList.setCurrentIndex(4)
+
+        cameraButton = QPushButton(self._barsWidget)
+        cameraButton.setText("Change camera preset")
+
+        zoomToSelectedButton = QPushButton(self._barsWidget)
+        zoomToSelectedButton.setText("Zoom to selected bar")
+
+        selectionModeList = QComboBox(self._barsWidget)
+        selectionModeList.addItem("None", QtGraphs3D.SelectionFlag.None_)
+        selectionModeList.addItem("Bar", QtGraphs3D.SelectionFlag.Item)
+        selectionModeList.addItem("Row", QtGraphs3D.SelectionFlag.Row)
+        sel = QtGraphs3D.SelectionFlag.ItemAndRow
+        selectionModeList.addItem("Bar and Row", sel)
+        selectionModeList.addItem("Column", QtGraphs3D.SelectionFlag.Column)
+        sel = QtGraphs3D.SelectionFlag.ItemAndColumn
+        selectionModeList.addItem("Bar and Column", sel)
+        sel = QtGraphs3D.SelectionFlag.RowAndColumn
+        selectionModeList.addItem("Row and Column", sel)
+        sel = QtGraphs3D.SelectionFlag.RowAndColumn
+        selectionModeList.addItem("Bar, Row and Column", sel)
+        sel = QtGraphs3D.SelectionFlag.Slice | QtGraphs3D.SelectionFlag.Row
+        selectionModeList.addItem("Slice into Row", sel)
+        sel = QtGraphs3D.SelectionFlag.Slice | QtGraphs3D.SelectionFlag.ItemAndRow
+        selectionModeList.addItem("Slice into Row and Item", sel)
+        sel = QtGraphs3D.SelectionFlag.Slice | QtGraphs3D.SelectionFlag.Column
+        selectionModeList.addItem("Slice into Column", sel)
+        sel = (QtGraphs3D.SelectionFlag.Slice
+               | QtGraphs3D.SelectionFlag.ItemAndColumn)
+        selectionModeList.addItem("Slice into Column and Item", sel)
+        sel = (QtGraphs3D.SelectionFlag.ItemRowAndColumn
+               | QtGraphs3D.SelectionFlag.MultiSeries)
+        selectionModeList.addItem("Multi: Bar, Row, Col", sel)
+        sel = (QtGraphs3D.SelectionFlag.Slice
+               | QtGraphs3D.SelectionFlag.ItemAndRow
+               | QtGraphs3D.SelectionFlag.MultiSeries)
+        selectionModeList.addItem("Multi, Slice: Row, Item", sel)
+        sel = (QtGraphs3D.SelectionFlag.Slice
+               | QtGraphs3D.SelectionFlag.ItemAndColumn
+               | QtGraphs3D.SelectionFlag.MultiSeries)
+        selectionModeList.addItem("Multi, Slice: Col, Item", sel)
+        selectionModeList.setCurrentIndex(1)
+
+        backgroundCheckBox = QCheckBox(self._barsWidget)
+        backgroundCheckBox.setText("Show background")
+        backgroundCheckBox.setChecked(False)
+
+        gridCheckBox = QCheckBox(self._barsWidget)
+        gridCheckBox.setText("Show grid")
+        gridCheckBox.setChecked(True)
+
+        seriesCheckBox = QCheckBox(self._barsWidget)
+        seriesCheckBox.setText("Show second series")
+        seriesCheckBox.setChecked(False)
+
+        reverseValueAxisCheckBox = QCheckBox(self._barsWidget)
+        reverseValueAxisCheckBox.setText("Reverse value axis")
+        reverseValueAxisCheckBox.setChecked(False)
+
+        rotationSliderX = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+        rotationSliderX.setTickInterval(30)
+        rotationSliderX.setTickPosition(QSlider.TickPosition.TicksBelow)
+        rotationSliderX.setMinimum(-180)
+        rotationSliderX.setValue(0)
+        rotationSliderX.setMaximum(180)
+        rotationSliderY = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+        rotationSliderY.setTickInterval(15)
+        rotationSliderY.setTickPosition(QSlider.TickPosition.TicksAbove)
+        rotationSliderY.setMinimum(-90)
+        rotationSliderY.setValue(0)
+        rotationSliderY.setMaximum(90)
+
+        fontSizeSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+        fontSizeSlider.setTickInterval(10)
+        fontSizeSlider.setTickPosition(QSlider.TickPosition.TicksBelow)
+        fontSizeSlider.setMinimum(1)
+        fontSizeSlider.setValue(30)
+        fontSizeSlider.setMaximum(100)
+
+        fontList = QFontComboBox(self._barsWidget)
+        fontList.setCurrentFont(QFont("Times New Roman"))
+
+        shadowQuality = QComboBox(self._barsWidget)
+        shadowQuality.addItem("None")
+        shadowQuality.addItem("Low")
+        shadowQuality.addItem("Medium")
+        shadowQuality.addItem("High")
+        shadowQuality.addItem("Low Soft")
+        shadowQuality.addItem("Medium Soft")
+        shadowQuality.addItem("High Soft")
+        shadowQuality.setCurrentIndex(5)
+
+        rangeList = QComboBox(self._barsWidget)
+        rangeList.addItem("2015")
+        rangeList.addItem("2016")
+        rangeList.addItem("2017")
+        rangeList.addItem("2018")
+        rangeList.addItem("2019")
+        rangeList.addItem("2020")
+        rangeList.addItem("2021")
+        rangeList.addItem("2022")
+        rangeList.addItem("All")
+        rangeList.setCurrentIndex(8)
+
+        axisTitlesVisibleCB = QCheckBox(self._barsWidget)
+        axisTitlesVisibleCB.setText("Axis titles visible")
+        axisTitlesVisibleCB.setChecked(True)
+
+        axisTitlesFixedCB = QCheckBox(self._barsWidget)
+        axisTitlesFixedCB.setText("Axis titles fixed")
+        axisTitlesFixedCB.setChecked(True)
+
+        axisLabelRotationSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+        axisLabelRotationSlider.setTickInterval(10)
+        axisLabelRotationSlider.setTickPosition(QSlider.TickPosition.TicksBelow)
+        axisLabelRotationSlider.setMinimum(0)
+        axisLabelRotationSlider.setValue(30)
+        axisLabelRotationSlider.setMaximum(90)
+
+        modeGroup = QButtonGroup(self._barsWidget)
+        modeWeather = QRadioButton("Temperature Data", self._barsWidget)
+        modeWeather.setChecked(True)
+        modeCustomProxy = QRadioButton("Custom Proxy Data", self._barsWidget)
+        modeGroup.addButton(modeWeather)
+        modeGroup.addButton(modeCustomProxy)
+
+        vLayout.addWidget(QLabel("Rotate horizontally"))
+        vLayout.addWidget(rotationSliderX, 0, Qt.AlignmentFlag.AlignTop)
+        vLayout.addWidget(QLabel("Rotate vertically"))
+        vLayout.addWidget(rotationSliderY, 0, Qt.AlignmentFlag.AlignTop)
+        vLayout.addWidget(labelButton, 0, Qt.AlignmentFlag.AlignTop)
+        vLayout.addWidget(cameraButton, 0, Qt.AlignmentFlag.AlignTop)
+        vLayout.addWidget(zoomToSelectedButton, 0, Qt.AlignmentFlag.AlignTop)
+        vLayout.addWidget(backgroundCheckBox)
+        vLayout.addWidget(gridCheckBox)
+        vLayout.addWidget(smoothCheckBox)
+        vLayout.addWidget(seriesCheckBox)
+        vLayout.addWidget(reverseValueAxisCheckBox)
+        vLayout.addWidget(axisTitlesVisibleCB)
+        vLayout.addWidget(axisTitlesFixedCB)
+        vLayout.addWidget(QLabel("Show year"))
+        vLayout.addWidget(rangeList)
+        vLayout.addWidget(QLabel("Change bar style"))
+        vLayout.addWidget(barStyleList)
+        vLayout.addWidget(QLabel("Change selection mode"))
+        vLayout.addWidget(selectionModeList)
+        vLayout.addWidget(QLabel("Change theme"))
+        vLayout.addWidget(themeList)
+        vLayout.addWidget(QLabel("Adjust shadow quality"))
+        vLayout.addWidget(shadowQuality)
+        vLayout.addWidget(QLabel("Change font"))
+        vLayout.addWidget(fontList)
+        vLayout.addWidget(QLabel("Adjust font size"))
+        vLayout.addWidget(fontSizeSlider)
+        vLayout.addWidget(QLabel("Axis label rotation"))
+        vLayout.addWidget(axisLabelRotationSlider, 0, Qt.AlignmentFlag.AlignTop)
+        vLayout.addWidget(modeWeather, 0, Qt.AlignmentFlag.AlignTop)
+        vLayout.addWidget(modeCustomProxy, 1, Qt.AlignmentFlag.AlignTop)
+
+        modifier = GraphModifier(barsGraph, self)
+        modifier.changeTheme(themeList.currentIndex())
+
+        rotationSliderX.valueChanged.connect(modifier.rotateX)
+        rotationSliderY.valueChanged.connect(modifier.rotateY)
+
+        labelButton.clicked.connect(modifier.changeLabelBackground)
+        cameraButton.clicked.connect(modifier.changePresetCamera)
+        zoomToSelectedButton.clicked.connect(modifier.zoomToSelectedBar)
+
+        backgroundCheckBox.checkStateChanged.connect(modifier.setPlotAreaBackgroundVisible)
+        gridCheckBox.checkStateChanged.connect(modifier.setGridVisible)
+        smoothCheckBox.checkStateChanged.connect(modifier.setSmoothBars)
+        seriesCheckBox.checkStateChanged.connect(modifier.setSeriesVisibility)
+        reverseValueAxisCheckBox.checkStateChanged.connect(modifier.setReverseValueAxis)
+
+        modifier.backgroundVisibleChanged.connect(backgroundCheckBox.setChecked)
+        modifier.gridVisibleChanged.connect(gridCheckBox.setChecked)
+
+        rangeList.currentIndexChanged.connect(modifier.changeRange)
+
+        barStyleList.currentIndexChanged.connect(modifier.changeStyle)
+
+        selectionModeList.currentIndexChanged.connect(modifier.changeSelectionMode)
+
+        themeList.currentIndexChanged.connect(modifier.changeTheme)
+
+        shadowQuality.currentIndexChanged.connect(modifier.changeShadowQuality)
+
+        modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+        barsGraph.shadowQualityChanged.connect(modifier.shadowQualityUpdatedByVisual)
+
+        fontSizeSlider.valueChanged.connect(modifier.changeFontSize)
+        fontList.currentFontChanged.connect(modifier.changeFont)
+
+        modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
+        modifier.fontChanged.connect(fontList.setCurrentFont)
+
+        axisTitlesVisibleCB.checkStateChanged.connect(modifier.setAxisTitleVisibility)
+        axisTitlesFixedCB.checkStateChanged.connect(modifier.setAxisTitleFixed)
+        axisLabelRotationSlider.valueChanged.connect(modifier.changeLabelRotation)
+
+        modeWeather.toggled.connect(modifier.setDataModeToWeather)
+        modeCustomProxy.toggled.connect(modifier.setDataModeToCustom)
+        modeWeather.toggled.connect(seriesCheckBox.setEnabled)
+        modeWeather.toggled.connect(rangeList.setEnabled)
+        modeWeather.toggled.connect(axisTitlesVisibleCB.setEnabled)
+        modeWeather.toggled.connect(axisTitlesFixedCB.setEnabled)
+        modeWeather.toggled.connect(axisLabelRotationSlider.setEnabled)
+
+    def barsWidget(self):
+        return self._barsWidget
diff --git a/examples/graphs/3d/widgetgraphgallery/data/layer_1.png b/examples/graphs/3d/widgetgraphgallery/data/layer_1.png
new file mode 100644 (file)
index 0000000..9138c71
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/layer_1.png differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/layer_2.png b/examples/graphs/3d/widgetgraphgallery/data/layer_2.png
new file mode 100644 (file)
index 0000000..61631ae
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/layer_2.png differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/layer_3.png b/examples/graphs/3d/widgetgraphgallery/data/layer_3.png
new file mode 100644 (file)
index 0000000..066ffbe
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/layer_3.png differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/license.txt b/examples/graphs/3d/widgetgraphgallery/data/license.txt
new file mode 100644 (file)
index 0000000..749daf3
--- /dev/null
@@ -0,0 +1,77 @@
+License information regarding the data obtained from National Land Survey of
+Finland http://www.maanmittauslaitos.fi/en
+- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
+  U4422C) 08/2014
+- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
+
+National Land Survey open data licence - version 1.0 - 1 May 2012
+
+1. General information
+
+The National Land Survey of Finland (hereinafter the Licensor), as the holder
+of the immaterial rights to the data, has granted on the terms mentioned below
+the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
+of it).
+
+The Licensee is a natural or legal person who makes use of the data covered by
+this licence. The Licensee accepts the terms of this licence by receiving the
+dataset(s) covered by the licence.
+
+This Licence agreement does not create a co-operation or business relationship
+between the Licensee and the Licensor.
+
+2. Terms of the licence
+
+2.1. Right of use
+
+This licence grants a worldwide, free of charge and irrevocable parallel right
+of use to open data. According to the terms of the licence, data received by
+the Licensee can be freely:
+ - copied, distributed and published,
+ - modified and utilised commercially and non-commercially,
+ - inserted into other products and
+ - used as a part of a software application or service.
+
+2.2. Duties and responsibilities of the Licensee
+
+Through reasonable means suitable to the distribution medium or method which is
+used in conjunction with a product containing data or a service utilising data
+covered by this licence or while distributing data, the Licensee shall:
+ - mention the name of the Licensor, the name of the dataset(s) and the time
+   when the National Land Survey has delivered the dataset(s) (e.g.: contains
+   data from the National Land Survey of Finland Topographic Database 06/2012)
+ - provide a copy of this licence or a link to it, as well as
+ - require third parties to provide the same information when granting rights
+   to copies of dataset(s) or products and services containing such data and
+ - remove the name of the Licensor from the product or service, if required to
+   do so by the Licensor.
+
+The terms of this licence do not allow the Licensee to state in conjunction
+with the use of dataset(s) that the Licensor supports or recommends such use.
+
+2.3. Duties and responsibilities of the Licensor
+
+The Licensor shall ensure that
+ - the Licensor has the right to grant rights to the dataset(s) in accordance
+   with this licence.
+
+The data has been licensed "as is" and the Licensor
+ - shall not be held responsible for any errors or omissions in the data,
+   disclaims any warranty for the validity or up to date status of the data and
+   shall be free from liability for direct or consequential damages arising
+   from the use of data provided by the Licensor,
+ - and is not obligated to ensure the continuous availability of the data, nor
+   to announce in advance the interruption or cessation of availability, and
+   the Licensor shall be free from liability for direct or consequential
+   damages arising from any such interruption or cessation.
+
+3. Jurisdiction
+
+Finnish law shall apply to this licence.
+
+4. Changes to this licence
+
+The Licensor may at any time change the terms of the licence or apply a
+different licence to the data. The terms of this licence shall, however, still
+apply to such data that has been received prior to the change of the terms of
+the licence or the licence itself.
diff --git a/examples/graphs/3d/widgetgraphgallery/data/maptexture.jpg b/examples/graphs/3d/widgetgraphgallery/data/maptexture.jpg
new file mode 100644 (file)
index 0000000..ae5d66e
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/maptexture.jpg differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/narrowarrow.mesh b/examples/graphs/3d/widgetgraphgallery/data/narrowarrow.mesh
new file mode 100644 (file)
index 0000000..288867b
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/narrowarrow.mesh differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/oilrig.mesh b/examples/graphs/3d/widgetgraphgallery/data/oilrig.mesh
new file mode 100644 (file)
index 0000000..4a7baed
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/oilrig.mesh differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/pipe.mesh b/examples/graphs/3d/widgetgraphgallery/data/pipe.mesh
new file mode 100644 (file)
index 0000000..984b6d4
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/pipe.mesh differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/raindata.txt b/examples/graphs/3d/widgetgraphgallery/data/raindata.txt
new file mode 100644 (file)
index 0000000..d955892
--- /dev/null
@@ -0,0 +1,158 @@
+# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu)
+# Format: year, month, rainfall
+2010,1, 0,
+2010,2, 3.4,
+2010,3, 52,
+2010,4, 33.8,
+2010,5, 45.6,
+2010,6, 43.8,
+2010,7, 104.6,
+2010,8, 105.4,
+2010,9, 107.2,
+2010,10,38.6,
+2010,11,17.8,
+2010,12,0,
+2011,1, 8.2,
+2011,2, 1.6,
+2011,3, 27.4,
+2011,4, 15.8,
+2011,5, 57.6,
+2011,6, 85.2,
+2011,7, 127,
+2011,8, 72.2,
+2011,9, 82.2,
+2011,10,62.4,
+2011,11,31.6,
+2011,12,53.8,
+2012,1, 0,
+2012,2, 5,
+2012,3, 32.4,
+2012,4, 57.6,
+2012,5, 71.4,
+2012,6, 60.8,
+2012,7, 109,
+2012,8, 43.6,
+2012,9, 79.4,
+2012,10,117.2,
+2012,11,59,
+2012,12,0.2,
+2013,1, 28,
+2013,2, 19,
+2013,3, 0,
+2013,4, 37.6,
+2013,5, 44.2,
+2013,6, 104.8,
+2013,7, 84.2,
+2013,8, 57.2,
+2013,9, 37.2,
+2013,10,64.6,
+2013,11,77.8,
+2013,12,92.8,
+2014,1, 23.8,
+2014,2, 23.6,
+2014,3, 15.4,
+2014,4, 13.2,
+2014,5, 36.4,
+2014,6, 26.4,
+2014,7, 95.8,
+2014,8, 81.8,
+2014,9, 13.8,
+2014,10,94.6,
+2014,11,44.6,
+2014,12,31,
+2015,1, 37.4,
+2015,2, 21,
+2015,3, 42,
+2015,4, 8.8,
+2015,5, 82.4,
+2015,6, 150,
+2015,7, 56.8,
+2015,8, 67.2,
+2015,9, 131.2,
+2015,10,38.4,
+2015,11,83.4,
+2015,12,47.8,
+2016,1, 12.4,
+2016,2, 34.8,
+2016,3, 29,
+2016,4, 40.4,
+2016,5, 32.4,
+2016,6, 80.2,
+2016,7, 102.6,
+2016,8, 95.6,
+2016,9, 40.2,
+2016,10,7.8,
+2016,11,39.6,
+2016,12,8.8,
+2017,1, 9.4,
+2017,2, 6.6,
+2017,3, 29,
+2017,4, 46.2,
+2017,5, 43.2,
+2017,6, 25.2,
+2017,7, 72.4,
+2017,8, 58.8,
+2017,9, 68.8,
+2017,10,45.8,
+2017,11,36.8,
+2017,12,29.6,
+2018,1, 19.8,
+2018,2, 0.8,
+2018,3, 4,
+2018,4, 23.2,
+2018,5, 13.2,
+2018,6, 62.8,
+2018,7, 33,
+2018,8, 96.6,
+2018,9, 72.6,
+2018,10,48.8,
+2018,11,31.8,
+2018,12,12.8,
+2019,1, 0.2,
+2019,2, 24.8,
+2019,3, 32,
+2019,4, 8.8,
+2019,5, 71.4,
+2019,6, 65.8,
+2019,7, 17.6,
+2019,8, 90,
+2019,9, 50,
+2019,10,77,
+2019,11,27,
+2019,12,43.2,
+2020,1, 28.8,
+2020,2, 45,
+2020,3, 18.6,
+2020,4, 13,
+2020,5, 30.8,
+2020,6, 21.4,
+2020,7, 163.6,
+2020,8, 12,
+2020,9, 102.4,
+2020,10,133.2,
+2020,11,69.8,
+2020,12,40.6,
+2021,1, 0.4,
+2021,2, 21.6,
+2021,3, 24,
+2021,4, 51.4,
+2021,5, 76.4,
+2021,6, 29.2,
+2021,7, 36.4,
+2021,8, 116,
+2021,9, 72.4,
+2021,10,93.4,
+2021,11,21,
+2021,12,10.2,
+2022,1, 8.6,
+2022,2, 6.6,
+2022,3, 5.2,
+2022,4, 15.2,
+2022,5, 37.6,
+2022,6, 45,
+2022,7, 67.4,
+2022,8, 161.6,
+2022,9, 22.8,
+2022,10,75.2,
+2022,11,21.8,
+2022,12,0.2
diff --git a/examples/graphs/3d/widgetgraphgallery/data/refinery.mesh b/examples/graphs/3d/widgetgraphgallery/data/refinery.mesh
new file mode 100644 (file)
index 0000000..a7e2493
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/refinery.mesh differ
diff --git a/examples/graphs/3d/widgetgraphgallery/data/topography.png b/examples/graphs/3d/widgetgraphgallery/data/topography.png
new file mode 100644 (file)
index 0000000..9349cdb
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/data/topography.png differ
diff --git a/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.rst b/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.rst
new file mode 100644 (file)
index 0000000..33a4da1
--- /dev/null
@@ -0,0 +1,11 @@
+Widget Graph Gallery
+====================
+
+
+Widget Graph Gallery demonstrates all three graph types and some of their
+special features. The graphs have their own tabs in the application.
+
+
+.. image:: widgetgraphgallery.webp
+   :width: 400
+   :alt: Widget Screenshot
diff --git a/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.webp b/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.webp
new file mode 100644 (file)
index 0000000..eb57672
Binary files /dev/null and b/examples/graphs/3d/widgetgraphgallery/doc/widgetgraphgallery.webp differ
diff --git a/examples/graphs/3d/widgetgraphgallery/graphmodifier.py b/examples/graphs/3d/widgetgraphgallery/graphmodifier.py
new file mode 100644 (file)
index 0000000..5c4d23c
--- /dev/null
@@ -0,0 +1,390 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+
+from math import atan, degrees
+import numpy as np
+
+from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Signal, Slot
+from PySide6.QtGui import QFont, QVector3D
+from PySide6.QtGraphs import (QAbstract3DSeries,
+                              QBarDataItem, QBar3DSeries, QCategory3DAxis,
+                              QValue3DAxis, QtGraphs3D, QGraphsTheme)
+
+from rainfalldata import RainfallData
+
+# Set up data
+TEMP_OULU = np.array([
+    [-7.4, -2.4, 0.0, 3.0, 8.2, 11.6, 14.7, 15.4, 11.4, 4.2, 2.1, -2.3],  # 2015
+    [-13.4, -3.9, -1.8, 3.1, 10.6, 13.7, 17.8, 13.6, 10.7, 3.5, -3.1, -4.2],  # 2016
+    [-5.7, -6.7, -3.0, -0.1, 4.7, 12.4, 16.1, 14.1, 9.4, 3.0, -0.3, -3.2],  # 2017
+    [-6.4, -11.9, -7.4, 1.9, 11.4, 12.4, 21.5, 16.1, 11.0, 4.4, 2.1, -4.1],  # 2018
+    [-11.7, -6.1, -2.4, 3.9, 7.2, 14.5, 15.6, 14.4, 8.5, 2.0, -3.0, -1.5],  # 2019
+    [-2.1, -3.4, -1.8, 0.6, 7.0, 17.1, 15.6, 15.4, 11.1, 5.6, 1.9, -1.7],  # 2020
+    [-9.6, -11.6, -3.2, 2.4, 7.8, 17.3, 19.4, 14.2, 8.0, 5.2, -2.2, -8.6],  # 2021
+    [-7.3, -6.4, -1.8, 1.3, 8.1, 15.5, 17.6, 17.6, 9.1, 5.4, -1.5, -4.4]],  # 2022
+    np.float64)
+
+
+TEMP_HELSINKI = np.array([
+    [-2.0, -0.1, 1.8, 5.1, 9.7, 13.7, 16.3, 17.3, 12.7, 5.4, 4.6, 2.1],  # 2015
+    [-10.3, -0.6, 0.0, 4.9, 14.3, 15.7, 17.7, 16.0, 12.7, 4.6, -1.0, -0.9],  # 2016
+    [-2.9, -3.3, 0.7, 2.3, 9.9, 13.8, 16.1, 15.9, 11.4, 5.0, 2.7, 0.7],  # 2017
+    [-2.2, -8.4, -4.7, 5.0, 15.3, 15.8, 21.2, 18.2, 13.3, 6.7, 2.8, -2.0],  # 2018
+    [-6.2, -0.5, -0.3, 6.8, 10.6, 17.9, 17.5, 16.8, 11.3, 5.2, 1.8, 1.4],  # 2019
+    [1.9, 0.5, 1.7, 4.5, 9.5, 18.4, 16.5, 16.8, 13.0, 8.2, 4.4, 0.9],  # 2020
+    [-4.7, -8.1, -0.9, 4.5, 10.4, 19.2, 20.9, 15.4, 9.5, 8.0, 1.5, -6.7],  # 2021
+    [-3.3, -2.2, -0.2, 3.3, 9.6, 16.9, 18.1, 18.9, 9.2, 7.6, 2.3, -3.4]],  # 2022
+    np.float64)
+
+
+class GraphModifier(QObject):
+
+    shadowQualityChanged = Signal(int)
+    backgroundVisibleChanged = Signal(bool)
+    gridVisibleChanged = Signal(bool)
+    fontChanged = Signal(QFont)
+    fontSizeChanged = Signal(int)
+
+    def __init__(self, bargraph, parent):
+        super().__init__(parent)
+        self._graph = bargraph
+        self._temperatureAxis = QValue3DAxis()
+        self._yearAxis = QCategory3DAxis()
+        self._monthAxis = QCategory3DAxis()
+        self._primarySeries = QBar3DSeries()
+        self._secondarySeries = QBar3DSeries()
+        self._celsiusString = "°C"
+
+        self._xRotation = float(0)
+        self._yRotation = float(0)
+        self._fontSize = 30
+        self._segments = 4
+        self._subSegments = 3
+        self._minval = float(-20)
+        self._maxval = float(20)
+        self._barMesh = QAbstract3DSeries.Mesh.BevelBar
+        self._smooth = False
+        self._animationCameraX = QPropertyAnimation()
+        self._animationCameraY = QPropertyAnimation()
+        self._animationCameraZoom = QPropertyAnimation()
+        self._animationCameraTarget = QPropertyAnimation()
+        self._defaultAngleX = float(0)
+        self._defaultAngleY = float(0)
+        self._defaultZoom = float(0)
+        self._defaultTarget = []
+        self._customData = None
+
+        self._graph.setShadowQuality(QtGraphs3D.ShadowQuality.SoftMedium)
+        theme = self._graph.activeTheme()
+        theme.setPlotAreaBackgroundVisible(False)
+        theme.setLabelFont(QFont("Times New Roman", self._fontSize))
+        theme.setLabelBackgroundVisible(True)
+        self._graph.setMultiSeriesUniform(True)
+
+        self._months = ["January", "February", "March", "April", "May", "June",
+                        "July", "August", "September", "October", "November",
+                        "December"]
+        self._years = ["2015", "2016", "2017", "2018", "2019", "2020",
+                       "2021", "2022"]
+
+        self._temperatureAxis.setTitle("Average temperature")
+        self._temperatureAxis.setSegmentCount(self._segments)
+        self._temperatureAxis.setSubSegmentCount(self._subSegments)
+        self._temperatureAxis.setRange(self._minval, self._maxval)
+        self._temperatureAxis.setLabelFormat("%.1f " + self._celsiusString)
+        self._temperatureAxis.setLabelAutoAngle(30.0)
+        self._temperatureAxis.setTitleVisible(True)
+
+        self._yearAxis.setTitle("Year")
+        self._yearAxis.setLabelAutoAngle(30.0)
+        self._yearAxis.setTitleVisible(True)
+        self._monthAxis.setTitle("Month")
+        self._monthAxis.setLabelAutoAngle(30.0)
+        self._monthAxis.setTitleVisible(True)
+
+        self._graph.setValueAxis(self._temperatureAxis)
+        self._graph.setRowAxis(self._yearAxis)
+        self._graph.setColumnAxis(self._monthAxis)
+
+        format = "Oulu - @colLabel @rowLabel: @valueLabel"
+        self._primarySeries.setItemLabelFormat(format)
+        self._primarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
+        self._primarySeries.setMeshSmooth(False)
+
+        format = "Helsinki - @colLabel @rowLabel: @valueLabel"
+        self._secondarySeries.setItemLabelFormat(format)
+        self._secondarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
+        self._secondarySeries.setMeshSmooth(False)
+        self._secondarySeries.setVisible(False)
+
+        self._graph.addSeries(self._primarySeries)
+        self._graph.addSeries(self._secondarySeries)
+
+        self.changePresetCamera()
+
+        self.resetTemperatureData()
+
+        # Set up property animations for zooming to the selected bar
+        self._defaultAngleX = self._graph.cameraXRotation()
+        self._defaultAngleY = self._graph.cameraYRotation()
+        self._defaultZoom = self._graph.cameraZoomLevel()
+        self._defaultTarget = self._graph.cameraTargetPosition()
+
+        self._animationCameraX.setTargetObject(self._graph)
+        self._animationCameraY.setTargetObject(self._graph)
+        self._animationCameraZoom.setTargetObject(self._graph)
+        self._animationCameraTarget.setTargetObject(self._graph)
+
+        self._animationCameraX.setPropertyName(b"cameraXRotation")
+        self._animationCameraY.setPropertyName(b"cameraYRotation")
+        self._animationCameraZoom.setPropertyName(b"cameraZoomLevel")
+        self._animationCameraTarget.setPropertyName(b"cameraTargetPosition")
+
+        duration = 1700
+        self._animationCameraX.setDuration(duration)
+        self._animationCameraY.setDuration(duration)
+        self._animationCameraZoom.setDuration(duration)
+        self._animationCameraTarget.setDuration(duration)
+
+        # The zoom always first zooms out above the graph and then zooms in
+        zoomOutFraction = 0.3
+        self._animationCameraX.setKeyValueAt(zoomOutFraction, 0.0)
+        self._animationCameraY.setKeyValueAt(zoomOutFraction, 90.0)
+        self._animationCameraZoom.setKeyValueAt(zoomOutFraction, 50.0)
+        self._animationCameraTarget.setKeyValueAt(zoomOutFraction,
+                                                  QVector3D(0, 0, 0))
+        self._customData = RainfallData()
+
+    def resetTemperatureData(self):
+        # Create data arrays
+        dataSet = []
+        dataSet2 = []
+
+        for year in range(0, len(self._years)):
+            # Create a data row
+            dataRow = []
+            dataRow2 = []
+            for month in range(0, len(self._months)):
+                # Add data to the row
+                item = QBarDataItem()
+                item.setValue(TEMP_OULU[year][month])
+                dataRow.append(item)
+                item = QBarDataItem()
+                item.setValue(TEMP_HELSINKI[year][month])
+                dataRow2.append(item)
+
+            # Add the row to the set
+            dataSet.append(dataRow)
+            dataSet2.append(dataRow2)
+
+        # Add data to the data proxy (the data proxy assumes ownership of it)
+        self._primarySeries.dataProxy().resetArray(dataSet, self._years, self._months)
+        self._secondarySeries.dataProxy().resetArray(dataSet2, self._years, self._months)
+
+    @Slot(int)
+    def changeRange(self, range):
+        if range >= len(self._years):
+            self._yearAxis.setRange(0, len(self._years) - 1)
+        else:
+            self._yearAxis.setRange(range, range)
+
+    @Slot(int)
+    def changeStyle(self, style):
+        comboBox = self.sender()
+        if comboBox:
+            self._barMesh = comboBox.itemData(style)
+            self._primarySeries.setMesh(self._barMesh)
+            self._secondarySeries.setMesh(self._barMesh)
+            self._customData.customSeries().setMesh(self._barMesh)
+
+    def changePresetCamera(self):
+        self._animationCameraX.stop()
+        self._animationCameraY.stop()
+        self._animationCameraZoom.stop()
+        self._animationCameraTarget.stop()
+
+        # Restore camera target in case animation has changed it
+        self._graph.setCameraTargetPosition(QVector3D(0.0, 0.0, 0.0))
+
+        self._preset = QtGraphs3D.CameraPreset.Front.value
+
+        self._graph.setCameraPreset(QtGraphs3D.CameraPreset(self._preset))
+
+        self._preset += 1
+        if self._preset > QtGraphs3D.CameraPreset.DirectlyBelow.value:
+            self._preset = QtGraphs3D.CameraPreset.FrontLow.value
+
+    @Slot(int)
+    def changeTheme(self, theme):
+        currentTheme = self._graph.activeTheme()
+        currentTheme.setTheme(QGraphsTheme.Theme(theme))
+        self.backgroundVisibleChanged.emit(currentTheme.isBackgroundVisible())
+        self.gridVisibleChanged.emit(currentTheme.isGridVisible())
+        self.fontChanged.emit(currentTheme.labelFont())
+        self.fontSizeChanged.emit(currentTheme.labelFont().pointSize())
+
+    def changeLabelBackground(self):
+        theme = self._graph.activeTheme()
+        theme.setLabelBackgroundVisible(not theme.isLabelBackgroundVisible())
+
+    @Slot(int)
+    def changeSelectionMode(self, selectionMode):
+        comboBox = self.sender()
+        if comboBox:
+            flags = comboBox.itemData(selectionMode)
+            self._graph.setSelectionMode(QtGraphs3D.SelectionFlags(flags))
+
+    def changeFont(self, font):
+        newFont = font
+        self._graph.activeTheme().setLabelFont(newFont)
+
+    def changeFontSize(self, fontsize):
+        self._fontSize = fontsize
+        font = self._graph.activeTheme().labelFont()
+        font.setPointSize(self._fontSize)
+        self._graph.activeTheme().setLabelFont(font)
+
+    @Slot(QtGraphs3D.ShadowQuality)
+    def shadowQualityUpdatedByVisual(self, sq):
+        # Updates the UI component to show correct shadow quality
+        self.shadowQualityChanged.emit(sq.value)
+
+    @Slot(int)
+    def changeLabelRotation(self, rotation):
+        self._temperatureAxis.setLabelAutoAngle(float(rotation))
+        self._monthAxis.setLabelAutoAngle(float(rotation))
+        self._yearAxis.setLabelAutoAngle(float(rotation))
+
+    @Slot(bool)
+    def setAxisTitleVisibility(self, state):
+        enabled = state == Qt.CheckState.Checked
+        self._temperatureAxis.setTitleVisible(enabled)
+        self._monthAxis.setTitleVisible(enabled)
+        self._yearAxis.setTitleVisible(enabled)
+
+    @Slot(bool)
+    def setAxisTitleFixed(self, state):
+        enabled = state == Qt.CheckState.Checked
+        self._temperatureAxis.setTitleFixed(enabled)
+        self._monthAxis.setTitleFixed(enabled)
+        self._yearAxis.setTitleFixed(enabled)
+
+    @Slot()
+    def zoomToSelectedBar(self):
+        self._animationCameraX.stop()
+        self._animationCameraY.stop()
+        self._animationCameraZoom.stop()
+        self._animationCameraTarget.stop()
+
+        currentX = self._graph.cameraXRotation()
+        currentY = self._graph.cameraYRotation()
+        currentZoom = self._graph.cameraZoomLevel()
+        currentTarget = self._graph.cameraTargetPosition()
+
+        self._animationCameraX.setStartValue(currentX)
+        self._animationCameraY.setStartValue(currentY)
+        self._animationCameraZoom.setStartValue(currentZoom)
+        self._animationCameraTarget.setStartValue(currentTarget)
+
+        selectedBar = (self._graph.selectedSeries().selectedBar()
+                       if self._graph.selectedSeries()
+                       else QBar3DSeries.invalidSelectionPosition())
+
+        if selectedBar != QBar3DSeries.invalidSelectionPosition():
+            # Normalize selected bar position within axis range to determine
+            # target coordinates
+            endTarget = QVector3D()
+            xMin = self._graph.columnAxis().min()
+            xRange = self._graph.columnAxis().max() - xMin
+            zMin = self._graph.rowAxis().min()
+            zRange = self._graph.rowAxis().max() - zMin
+            endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0 - 1.0)
+            endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0 - 1.0)
+
+            # Rotate the camera so that it always points approximately to the
+            # graph center
+            endAngleX = 90.0 - degrees(atan(float(endTarget.z() / endTarget.x())))
+            if endTarget.x() > 0.0:
+                endAngleX -= 180.0
+            proxy = self._graph.selectedSeries().dataProxy()
+            barValue = proxy.itemAt(selectedBar.x(), selectedBar.y()).value()
+            endAngleY = 30.0 if barValue >= 0.0 else -30.0
+            if self._graph.valueAxis().reversed():
+                endAngleY *= -1.0
+
+            self._animationCameraX.setEndValue(float(endAngleX))
+            self._animationCameraY.setEndValue(endAngleY)
+            self._animationCameraZoom.setEndValue(250)
+            self._animationCameraTarget.setEndValue(endTarget)
+        else:
+            # No selected bar, so return to the default view
+            self._animationCameraX.setEndValue(self._defaultAngleX)
+            self._animationCameraY.setEndValue(self._defaultAngleY)
+            self._animationCameraZoom.setEndValue(self._defaultZoom)
+            self._animationCameraTarget.setEndValue(self._defaultTarget)
+
+        self._animationCameraX.start()
+        self._animationCameraY.start()
+        self._animationCameraZoom.start()
+        self._animationCameraTarget.start()
+
+    @Slot(bool)
+    def setDataModeToWeather(self, enabled):
+        if enabled:
+            self.changeDataMode(False)
+
+    @Slot(bool)
+    def setDataModeToCustom(self, enabled):
+        if enabled:
+            self.changeDataMode(True)
+
+    def changeShadowQuality(self, quality):
+        sq = QtGraphs3D.ShadowQuality(quality)
+        self._graph.setShadowQuality(sq)
+        self.shadowQualityChanged.emit(quality)
+
+    def rotateX(self, rotation):
+        self._xRotation = rotation
+        self._graph.setCameraPosition(self._xRotation, self._yRotation)
+
+    def rotateY(self, rotation):
+        self._yRotation = rotation
+        self._graph.setCameraPosition(self._xRotation, self._yRotation)
+
+    def setPlotAreaBackgroundVisible(self, state):
+        enabled = state == Qt.CheckState.Checked
+        self._graph.activeTheme().setPlotAreaBackgroundVisible(enabled)
+
+    def setGridVisible(self, state):
+        self._graph.activeTheme().setGridVisible(state == Qt.CheckState.Checked)
+
+    def setSmoothBars(self, state):
+        self._smooth = state == Qt.CheckState.Checked
+        self._primarySeries.setMeshSmooth(self._smooth)
+        self._secondarySeries.setMeshSmooth(self._smooth)
+        self._customData.customSeries().setMeshSmooth(self._smooth)
+
+    def setSeriesVisibility(self, state):
+        self._secondarySeries.setVisible(state == Qt.CheckState.Checked)
+
+    def setReverseValueAxis(self, state):
+        self._graph.valueAxis().setReversed(state == Qt.CheckState.Checked)
+
+    def changeDataMode(self, customData):
+        # Change between weather data and data from custom proxy
+        if customData:
+            self._graph.removeSeries(self._primarySeries)
+            self._graph.removeSeries(self._secondarySeries)
+            self._graph.addSeries(self._customData.customSeries())
+            self._graph.setValueAxis(self._customData.valueAxis())
+            self._graph.setRowAxis(self._customData.rowAxis())
+            self._graph.setColumnAxis(self._customData.colAxis())
+        else:
+            self._graph.removeSeries(self._customData.customSeries())
+            self._graph.addSeries(self._primarySeries)
+            self._graph.addSeries(self._secondarySeries)
+            self._graph.setValueAxis(self._temperatureAxis)
+            self._graph.setRowAxis(self._yearAxis)
+            self._graph.setColumnAxis(self._monthAxis)
diff --git a/examples/graphs/3d/widgetgraphgallery/highlightseries.py b/examples/graphs/3d/widgetgraphgallery/highlightseries.py
new file mode 100644 (file)
index 0000000..27dad47
--- /dev/null
@@ -0,0 +1,95 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QPoint, Qt, Slot
+from PySide6.QtGui import QLinearGradient, QVector3D
+from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem,
+                              QGraphsTheme)
+
+
+DARK_RED_POS = 1.0
+RED_POS = 0.8
+YELLOW_POS = 0.6
+GREEN_POS = 0.4
+DARK_GREEN_POS = 0.2
+
+
+class HighlightSeries(QSurface3DSeries):
+
+    def __init__(self):
+        super().__init__()
+        self._width = 100
+        self._height = 100
+        self._srcWidth = 0
+        self._srcHeight = 0
+        self._position = {}
+        self._topographicSeries = None
+        self._minHeight = 0.0
+        self.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+        self.setShading(QSurface3DSeries.Shading.Flat)
+        self.setVisible(False)
+
+    def setTopographicSeries(self, series):
+        self._topographicSeries = series
+        array = self._topographicSeries.dataArray()
+        self._srcWidth = len(array[0])
+        self._srcHeight = len(array)
+        self._topographicSeries.selectedPointChanged.connect(self.handlePositionChange)
+
+    def setMinHeight(self, height):
+        self. m_minHeight = height
+
+    @Slot(QPoint)
+    def handlePositionChange(self, position):
+        self._position = position
+
+        if position == self.invalidSelectionPosition():
+            self.setVisible(False)
+            return
+
+        halfWidth = self._width / 2
+        halfHeight = self._height / 2
+
+        startX = position.y() - halfWidth
+        if startX < 0:
+            startX = 0
+        endX = position.y() + halfWidth
+        if endX > (self._srcWidth - 1):
+            endX = self._srcWidth - 1
+        startZ = position.x() - halfHeight
+        if startZ < 0:
+            startZ = 0
+        endZ = position.x() + halfHeight
+        if endZ > (self._srcHeight - 1):
+            endZ = self._srcHeight - 1
+
+        srcArray = self._topographicSeries.dataArray()
+
+        dataArray = []
+        for i in range(int(startZ), int(endZ)):
+            newRow = []
+            srcRow = srcArray[i]
+            for j in range(startX, endX):
+                pos = srcRow.at(j).position()
+                pos.setY(pos.y() + 0.1)
+                item = QSurfaceDataItem(QVector3D(pos))
+                newRow.append(item)
+            dataArray.append(newRow)
+        self.dataProxy().resetArray(dataArray)
+        self.setVisible(True)
+
+    @Slot(float)
+    def handleGradientChange(self, value):
+        ratio = self._minHeight / value
+
+        gr = QLinearGradient()
+        gr.setColorAt(0.0, Qt.GlobalColor.black)
+        gr.setColorAt(DARK_GREEN_POS * ratio, Qt.GlobalColor.darkGreen)
+        gr.setColorAt(GREEN_POS * ratio, Qt.GlobalColor.green)
+        gr.setColorAt(YELLOW_POS * ratio, Qt.GlobalColor.yellow)
+        gr.setColorAt(RED_POS * ratio, Qt.GlobalColor.red)
+        gr.setColorAt(DARK_RED_POS * ratio, Qt.GlobalColor.darkRed)
+
+        self.setBaseGradient(gr)
+        self.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
diff --git a/examples/graphs/3d/widgetgraphgallery/main.py b/examples/graphs/3d/widgetgraphgallery/main.py
new file mode 100644 (file)
index 0000000..7a5bfe4
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+"""PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
+
+import sys
+
+from PySide6.QtCore import QSize
+from PySide6.QtWidgets import QApplication, QTabWidget
+
+from bargraph import BarGraph
+from scattergraph import ScatterGraph
+from surfacegraph import SurfaceGraph
+
+
+class MainWidget(QTabWidget):
+    """Tab widget for creating own tabs for Q3DBars, Q3DScatter, and Q3DSurface"""
+
+    def __init__(self, p=None):
+        super().__init__(p)
+
+        screen_size = self.screen().size()
+        minimum_graph_size = QSize(screen_size.width() / 2, screen_size.height() / 1.75)
+
+        # Create bar graph
+        self._bars = BarGraph(minimum_graph_size, screen_size)
+        # Create scatter graph
+        self._scatter = ScatterGraph(minimum_graph_size, screen_size)
+        # Create surface graph
+        self._surface = SurfaceGraph(minimum_graph_size, screen_size)
+
+        # Add bars widget
+        self.addTab(self._bars.barsWidget(), "Bar Graph")
+        # Add scatter widget
+        self.addTab(self._scatter.scatterWidget(), "Scatter Graph")
+        # Add surface widget
+        self.addTab(self._surface.surfaceWidget(), "Surface Graph")
+
+
+if __name__ == "__main__":
+    app = QApplication(sys.argv)
+
+    tabWidget = MainWidget()
+    tabWidget.setWindowTitle("Widget Gallery")
+
+    tabWidget.show()
+    exit_code = app.exec()
+    del tabWidget
+    sys.exit(exit_code)
diff --git a/examples/graphs/3d/widgetgraphgallery/rainfalldata.py b/examples/graphs/3d/widgetgraphgallery/rainfalldata.py
new file mode 100644 (file)
index 0000000..a533967
--- /dev/null
@@ -0,0 +1,126 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+import sys
+
+from pathlib import Path
+
+from PySide6.QtCore import QFile, QIODevice, QObject
+from PySide6.QtGraphs import (QBar3DSeries, QCategory3DAxis, QValue3DAxis)
+
+from variantbardataproxy import VariantBarDataProxy
+from variantbardatamapping import VariantBarDataMapping
+from variantdataset import VariantDataSet
+
+
+MONTHS = ["January", "February", "March", "April",
+          "May", "June", "July", "August", "September", "October",
+          "November", "December"]
+
+
+class RainfallData(QObject):
+
+    def __init__(self):
+        super().__init__()
+        self._columnCount = 0
+        self._rowCount = 0
+        self._years = []
+        self._numericMonths = []
+        self._proxy = VariantBarDataProxy()
+        self._mapping = None
+        self._dataSet = None
+        self._series = QBar3DSeries()
+        self._valueAxis = QValue3DAxis()
+        self._rowAxis = QCategory3DAxis()
+        self._colAxis = QCategory3DAxis()
+
+        # In data file the months are in numeric format, so create custom list
+        for i in range(1, 13):
+            self._numericMonths.append(str(i))
+
+        self._columnCount = len(self._numericMonths)
+
+        self.updateYearsList(2010, 2022)
+
+        # Create proxy and series
+        self._proxy = VariantBarDataProxy()
+        self._series = QBar3DSeries(self._proxy)
+
+        self._series.setItemLabelFormat("%.1f mm")
+
+        # Create the axes
+        self._rowAxis = QCategory3DAxis(self)
+        self._colAxis = QCategory3DAxis(self)
+        self._valueAxis = QValue3DAxis(self)
+        self._rowAxis.setAutoAdjustRange(True)
+        self._colAxis.setAutoAdjustRange(True)
+        self._valueAxis.setAutoAdjustRange(True)
+
+        # Set axis labels and titles
+        self._rowAxis.setTitle("Year")
+        self._colAxis.setTitle("Month")
+        self._valueAxis.setTitle("rainfall (mm)")
+        self._valueAxis.setSegmentCount(5)
+        self._rowAxis.setLabels(self._years)
+        self._colAxis.setLabels(MONTHS)
+        self._rowAxis.setTitleVisible(True)
+        self._colAxis.setTitleVisible(True)
+        self._valueAxis.setTitleVisible(True)
+
+        self.addDataSet()
+
+    def customSeries(self):
+        return self._series
+
+    def valueAxis(self):
+        return self._valueAxis
+
+    def rowAxis(self):
+        return self._rowAxis
+
+    def colAxis(self):
+        return self._colAxis
+
+    def updateYearsList(self, start, end):
+        self._years.clear()
+        for i in range(start, end + 1):
+            self._years.append(str(i))
+        self._rowCount = len(self._years)
+
+    def addDataSet(self):
+        # Create a new variant data set and data item list
+        self._dataSet = VariantDataSet()
+        itemList = []
+
+        # Read data from a data file into the data item list
+        file_path = Path(__file__).resolve().parent / "data" / "raindata.txt"
+        dataFile = QFile(file_path)
+        if dataFile.open(QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.Text):
+            data = dataFile.readAll().data().decode("utf8")
+            for line in data.split("\n"):
+                if line and not line.startswith("#"):  # Ignore comments
+                    tokens = line.split(",")
+                    # Each line has three data items: Year, month, and
+                    # rainfall value
+                    if len(tokens) >= 3:
+                        # Store year and month as strings, and rainfall value
+                        # as double into a variant data item and add the item to
+                        # the item list.
+                        newItem = []
+                        newItem.append(tokens[0].strip())
+                        newItem.append(tokens[1].strip())
+                        newItem.append(float(tokens[2].strip()))
+                        itemList.append(newItem)
+        else:
+            print("Unable to open data file:", dataFile.fileName(),
+                  file=sys.stderr)
+
+        # Add items to the data set and set it to the proxy
+        self._dataSet.addItems(itemList)
+        self._proxy.setDataSet(self._dataSet)
+
+        # Create new mapping for the data and set it to the proxy
+        self._mapping = VariantBarDataMapping(0, 1, 2,
+                                              self._years, self._numericMonths)
+        self._proxy.setMapping(self._mapping)
diff --git a/examples/graphs/3d/widgetgraphgallery/scatterdatamodifier.py b/examples/graphs/3d/widgetgraphgallery/scatterdatamodifier.py
new file mode 100644 (file)
index 0000000..946c962
--- /dev/null
@@ -0,0 +1,199 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from enum import Enum
+from math import sin, cos, degrees, sqrt
+
+from PySide6.QtCore import QObject, Signal, Slot, Qt
+from PySide6.QtGui import QVector2D, QVector3D
+from PySide6.QtGraphs import (QAbstract3DSeries,
+                              QScatterDataItem, QScatterDataProxy,
+                              QScatter3DSeries, QtGraphs3D, QGraphsTheme)
+
+
+NUMBER_OF_ITEMS = 10000
+CURVE_DIVIDER = 7.5
+LOWER_NUMBER_OF_ITEMS = 900
+LOWER_CURVE_DIVIDER = 0.75
+
+
+class InputState(Enum):
+    StateNormal = 0
+    StateDraggingX = 1
+    StateDraggingZ = 2
+    StateDraggingY = 3
+
+
+class ScatterDataModifier(QObject):
+
+    backgroundEnabledChanged = Signal(bool)
+    gridVisibleChanged = Signal(bool)
+    shadowQualityChanged = Signal(int)
+
+    def __init__(self, scatter, parent):
+        super().__init__(parent)
+
+        self._graph = scatter
+
+        self._style = QAbstract3DSeries.Mesh.Sphere
+        self._smooth = True
+        self._autoAdjust = True
+        self._itemCount = LOWER_NUMBER_OF_ITEMS
+        self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
+
+        self._graph.activeTheme().setTheme(QGraphsTheme.Theme.MixSeries)
+        self._graph.activeTheme().setColorScheme(QGraphsTheme.ColorScheme.Dark)
+        self._graph.setShadowQuality(QtGraphs3D.ShadowQuality.SoftHigh)
+        self._graph.setCameraPreset(QtGraphs3D.CameraPreset.Front)
+        self._graph.setCameraZoomLevel(80.0)
+
+        self._proxy = QScatterDataProxy()
+        self._series = QScatter3DSeries(self._proxy)
+        self._series.setItemLabelFormat("@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel")
+        self._series.setMeshSmooth(self._smooth)
+        self._graph.addSeries(self._series)
+        self._preset = QtGraphs3D.CameraPreset.FrontLow.value
+
+        self._state = InputState.StateNormal
+        self._dragSpeedModifier = float(15)
+
+        self._graph.selectedElementChanged.connect(self.handleElementSelected)
+        self._graph.dragged.connect(self.handleAxisDragging)
+        self._graph.setDragButton(Qt.MouseButton.LeftButton)
+
+        self.addData()
+
+    def addData(self):
+        # Configure the axes according to the data
+        self._graph.axisX().setTitle("X")
+        self._graph.axisY().setTitle("Y")
+        self._graph.axisZ().setTitle("Z")
+
+        dataArray = []
+        limit = int(sqrt(self._itemCount) / 2.0)
+        for i in range(-limit, limit):
+            for j in range(-limit, limit):
+                x = float(i) + 0.5
+                y = cos(degrees(float(i * j) / self._CURVE_DIVIDER))
+                z = float(j) + 0.5
+                dataArray.append(QScatterDataItem(QVector3D(x, y, z)))
+
+        self._graph.seriesList()[0].dataProxy().resetArray(dataArray)
+
+    @Slot(int)
+    def changeStyle(self, style):
+        comboBox = self.sender()
+        if comboBox:
+            self._style = comboBox.itemData(style)
+            if self._graph.seriesList():
+                self._graph.seriesList()[0].setMesh(self._style)
+
+    @Slot(int)
+    def setSmoothDots(self, smooth):
+        self._smooth = smooth == Qt.CheckState.Checked
+        series = self._graph.seriesList()[0]
+        series.setMeshSmooth(self._smooth)
+
+    @Slot(int)
+    def changeTheme(self, theme):
+        currentTheme = self._graph.activeTheme()
+        currentTheme.setTheme(QGraphsTheme.Theme(theme))
+        self.backgroundEnabledChanged.emit(currentTheme.isPlotAreaBackgroundVisible())
+        self.gridVisibleChanged.emit(currentTheme.isGridVisible())
+
+    @Slot()
+    def changePresetCamera(self):
+        self._graph.setCameraPreset(QtGraphs3D.CameraPreset(self._preset))
+
+        self._preset += 1
+        if self._preset > QtGraphs3D.CameraPreset.DirectlyBelow.value:
+            self._preset = QtGraphs3D.CameraPreset.FrontLow.value
+
+    @Slot(QtGraphs3D.ShadowQuality)
+    def shadowQualityUpdatedByVisual(self, sq):
+        self.shadowQualityChanged.emit(sq.value)
+
+    @Slot(int)
+    def changeShadowQuality(self, quality):
+        sq = QtGraphs3D.ShadowQuality(quality)
+        self._graph.setShadowQuality(sq)
+
+    @Slot(int)
+    def setPlotAreaBackgroundVisible(self, state):
+        enabled = state == Qt.CheckState.Checked
+        self._graph.activeTheme().setPlotAreaBackgroundVisible(enabled)
+
+    @Slot(int)
+    def setGridVisible(self, state):
+        self._graph.activeTheme().setGridVisible(state == Qt.Checked.value)
+
+    @Slot()
+    def toggleItemCount(self):
+        if self._itemCount == NUMBER_OF_ITEMS:
+            self._itemCount = LOWER_NUMBER_OF_ITEMS
+            self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
+        else:
+            self._itemCount = NUMBER_OF_ITEMS
+            self._CURVE_DIVIDER = CURVE_DIVIDER
+
+        self._graph.seriesList()[0].dataProxy().resetArray([])
+        self.addData()
+
+    @Slot()
+    def toggleRanges(self):
+        if not self._autoAdjust:
+            self._graph.axisX().setAutoAdjustRange(True)
+            self._graph.axisZ().setAutoAdjustRange(True)
+            self._dragSpeedModifier = 1.5
+            self._autoAdjust = True
+        else:
+            self._graph.axisX().setRange(-10.0, 10.0)
+            self._graph.axisZ().setRange(-10.0, 10.0)
+            self._dragSpeedModifier = float(15)
+            self._autoAdjust = False
+
+    @Slot(QtGraphs3D.ElementType)
+    def handleElementSelected(self, type):
+        if type == QtGraphs3D.ElementType.AxisXLabel:
+            self._state = InputState.StateDraggingX
+        elif type == QtGraphs3D.ElementType.AxisYLabel:
+            self._state = InputState.StateDraggingY
+        elif type == QtGraphs3D.ElementType.AxisZLabel:
+            self._state = InputState.StateDraggingZ
+        else:
+            self._state = InputState.StateNormal
+
+    @Slot(QVector2D)
+    def handleAxisDragging(self, delta):
+        distance = 0.0
+        # Get scene orientation from active camera
+        xRotation = self._graph.cameraXRotation()
+        yRotation = self._graph.cameraYRotation()
+
+        # Calculate directional drag multipliers based on rotation
+        xMulX = cos(degrees(xRotation))
+        xMulY = sin(degrees(xRotation))
+        zMulX = sin(degrees(xRotation))
+        zMulY = cos(degrees(xRotation))
+
+        # Get the drag amount
+        move = delta.toPoint()
+
+        # Flip the effect of y movement if we're viewing from below
+        yMove = -move.y() if yRotation < 0 else move.y()
+
+        # Adjust axes
+        if self._state == InputState.StateDraggingX:
+            axis = self._graph.axisX()
+            distance = (move.x() * xMulX - yMove * xMulY) / self._dragSpeedModifier
+            axis.setRange(axis.min() - distance, axis.max() - distance)
+        elif self._state == InputState.StateDraggingZ:
+            axis = self._graph.axisZ()
+            distance = (move.x() * zMulX + yMove * zMulY) / self._dragSpeedModifier
+            axis.setRange(axis.min() + distance, axis.max() + distance)
+        elif self._state == InputState.StateDraggingY:
+            axis = self._graph.axisY()
+            # No need to use adjusted y move here
+            distance = move.y() / self._dragSpeedModifier
+            axis.setRange(axis.min() + distance, axis.max() + distance)
diff --git a/examples/graphs/3d/widgetgraphgallery/scattergraph.py b/examples/graphs/3d/widgetgraphgallery/scattergraph.py
new file mode 100644 (file)
index 0000000..1b5c507
--- /dev/null
@@ -0,0 +1,125 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QObject, QSize, Qt
+from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
+                               QLabel, QHBoxLayout, QSizePolicy,
+                               QVBoxLayout, QWidget, )
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import QAbstract3DSeries
+from PySide6.QtGraphsWidgets import Q3DScatterWidgetItem
+
+from scatterdatamodifier import ScatterDataModifier
+
+
+class ScatterGraph(QObject):
+
+    def __init__(self, minimum_graph_size, maximum_graph_size):
+        super().__init__()
+
+        scatterGraph = Q3DScatterWidgetItem()
+        scatterGraphWidget = QQuickWidget()
+        scatterGraph.setWidget(scatterGraphWidget)
+        self._scatterWidget = QWidget()
+        hLayout = QHBoxLayout(self._scatterWidget)
+        scatterGraphWidget.setMinimumSize(minimum_graph_size)
+        scatterGraphWidget.setMaximumSize(maximum_graph_size)
+        scatterGraphWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+        scatterGraphWidget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+        hLayout.addWidget(scatterGraphWidget, 1)
+
+        vLayout = QVBoxLayout()
+        hLayout.addLayout(vLayout)
+
+        cameraButton = QCommandLinkButton(self._scatterWidget)
+        cameraButton.setText("Change camera preset")
+        cameraButton.setDescription("Switch between a number of preset camera positions")
+        cameraButton.setIconSize(QSize(0, 0))
+
+        itemCountButton = QCommandLinkButton(self._scatterWidget)
+        itemCountButton.setText("Toggle item count")
+        itemCountButton.setDescription("Switch between 900 and 10000 data points")
+        itemCountButton.setIconSize(QSize(0, 0))
+
+        rangeButton = QCommandLinkButton(self._scatterWidget)
+        rangeButton.setText("Toggle axis ranges")
+        rangeButton.setDescription("Switch between automatic axis ranges and preset ranges")
+        rangeButton.setIconSize(QSize(0, 0))
+
+        backgroundCheckBox = QCheckBox(self._scatterWidget)
+        backgroundCheckBox.setText("Show graph background")
+        backgroundCheckBox.setChecked(True)
+
+        gridCheckBox = QCheckBox(self._scatterWidget)
+        gridCheckBox.setText("Show grid")
+        gridCheckBox.setChecked(True)
+
+        smoothCheckBox = QCheckBox(self._scatterWidget)
+        smoothCheckBox.setText("Smooth dots")
+        smoothCheckBox.setChecked(True)
+
+        itemStyleList = QComboBox(self._scatterWidget)
+        itemStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
+        itemStyleList.addItem("Cube", QAbstract3DSeries.Mesh.Cube)
+        itemStyleList.addItem("Minimal", QAbstract3DSeries.Mesh.Minimal)
+        itemStyleList.addItem("Point", QAbstract3DSeries.Mesh.Point)
+        itemStyleList.setCurrentIndex(0)
+
+        themeList = QComboBox(self._scatterWidget)
+        themeList.addItem("Qt")
+        themeList.addItem("Primary Colors")
+        themeList.addItem("Digia")
+        themeList.addItem("Stone Moss")
+        themeList.addItem("Army Blue")
+        themeList.addItem("Retro")
+        themeList.addItem("Ebony")
+        themeList.addItem("Isabelle")
+        themeList.setCurrentIndex(3)
+
+        shadowQuality = QComboBox(self._scatterWidget)
+        shadowQuality.addItem("None")
+        shadowQuality.addItem("Low")
+        shadowQuality.addItem("Medium")
+        shadowQuality.addItem("High")
+        shadowQuality.addItem("Low Soft")
+        shadowQuality.addItem("Medium Soft")
+        shadowQuality.addItem("High Soft")
+        shadowQuality.setCurrentIndex(6)
+
+        vLayout.addWidget(cameraButton)
+        vLayout.addWidget(itemCountButton)
+        vLayout.addWidget(rangeButton)
+        vLayout.addWidget(backgroundCheckBox)
+        vLayout.addWidget(gridCheckBox)
+        vLayout.addWidget(smoothCheckBox)
+        vLayout.addWidget(QLabel("Change dot style"))
+        vLayout.addWidget(itemStyleList)
+        vLayout.addWidget(QLabel("Change theme"))
+        vLayout.addWidget(themeList)
+        vLayout.addWidget(QLabel("Adjust shadow quality"))
+        vLayout.addWidget(shadowQuality, 1, Qt.AlignmentFlag.AlignTop)
+
+        modifier = ScatterDataModifier(scatterGraph, self)
+
+        cameraButton.clicked.connect(modifier.changePresetCamera)
+        itemCountButton.clicked.connect(modifier.toggleItemCount)
+        rangeButton.clicked.connect(modifier.toggleRanges)
+
+        backgroundCheckBox.checkStateChanged.connect(modifier.setPlotAreaBackgroundVisible)
+        gridCheckBox.checkStateChanged.connect(modifier.setGridVisible)
+        smoothCheckBox.checkStateChanged.connect(modifier.setSmoothDots)
+
+        modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
+        modifier.gridVisibleChanged.connect(gridCheckBox.setChecked)
+        itemStyleList.currentIndexChanged.connect(modifier.changeStyle)
+
+        themeList.currentIndexChanged.connect(modifier.changeTheme)
+
+        shadowQuality.currentIndexChanged.connect(modifier.changeShadowQuality)
+
+        modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+        scatterGraph.shadowQualityChanged.connect(modifier.shadowQualityUpdatedByVisual)
+
+    def scatterWidget(self):
+        return self._scatterWidget
diff --git a/examples/graphs/3d/widgetgraphgallery/surfacegraph.py b/examples/graphs/3d/widgetgraphgallery/surfacegraph.py
new file mode 100644 (file)
index 0000000..8d565f9
--- /dev/null
@@ -0,0 +1,260 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from surfacegraphmodifier import SurfaceGraphModifier
+
+from PySide6.QtCore import QObject, Qt
+from PySide6.QtGui import QBrush, QIcon, QLinearGradient, QPainter, QPixmap
+from PySide6.QtWidgets import (QGroupBox, QCheckBox, QLabel, QHBoxLayout,
+                               QPushButton, QRadioButton, QSizePolicy, QSlider,
+                               QVBoxLayout, QWidget)
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphsWidgets import Q3DSurfaceWidgetItem
+
+
+def gradientBtoYPB_Pixmap():
+    grBtoY = QLinearGradient(0, 0, 1, 100)
+    grBtoY.setColorAt(1.0, Qt.GlobalColor.black)
+    grBtoY.setColorAt(0.67, Qt.GlobalColor.blue)
+    grBtoY.setColorAt(0.33, Qt.GlobalColor.red)
+    grBtoY.setColorAt(0.0, Qt.GlobalColor.yellow)
+    pm = QPixmap(24, 100)
+    with QPainter(pm) as pmp:
+        pmp.setBrush(QBrush(grBtoY))
+        pmp.setPen(Qt.PenStyle.NoPen)
+        pmp.drawRect(0, 0, 24, 100)
+    return pm
+
+
+def gradientGtoRPB_Pixmap():
+    grGtoR = QLinearGradient(0, 0, 1, 100)
+    grGtoR.setColorAt(1.0, Qt.GlobalColor.darkGreen)
+    grGtoR.setColorAt(0.5, Qt.GlobalColor.yellow)
+    grGtoR.setColorAt(0.2, Qt.GlobalColor.red)
+    grGtoR.setColorAt(0.0, Qt.GlobalColor.darkRed)
+    pm = QPixmap(24, 100)
+    with QPainter(pm) as pmp:
+        pmp.setBrush(QBrush(grGtoR))
+        pmp.setPen(Qt.PenStyle.NoPen)
+        pmp.drawRect(0, 0, 24, 100)
+    return pm
+
+
+def highlightPixmap():
+    HEIGHT = 400
+    WIDTH = 110
+    BORDER = 10
+    gr = QLinearGradient(0, 0, 1, HEIGHT - 2 * BORDER)
+    gr.setColorAt(1.0, Qt.GlobalColor.black)
+    gr.setColorAt(0.8, Qt.GlobalColor.darkGreen)
+    gr.setColorAt(0.6, Qt.GlobalColor.green)
+    gr.setColorAt(0.4, Qt.GlobalColor.yellow)
+    gr.setColorAt(0.2, Qt.GlobalColor.red)
+    gr.setColorAt(0.0, Qt.GlobalColor.darkRed)
+    pmHighlight = QPixmap(WIDTH, HEIGHT)
+    pmHighlight.fill(Qt.GlobalColor.transparent)
+    with QPainter(pmHighlight) as pmpHighlight:
+        pmpHighlight.setBrush(QBrush(gr))
+        pmpHighlight.setPen(Qt.PenStyle.NoPen)
+        pmpHighlight.drawRect(BORDER, BORDER, 35, HEIGHT - 2 * BORDER)
+        pmpHighlight.setPen(Qt.GlobalColor.black)
+        step = (HEIGHT - 2 * BORDER) / 5
+        for i in range(0, 6):
+            yPos = i * step + BORDER
+            pmpHighlight.drawLine(BORDER, yPos, 55, yPos)
+            HEIGHT = 550 - (i * 110)
+            pmpHighlight.drawText(60, yPos + 2, f"{HEIGHT} m")
+    return pmHighlight
+
+
+class SurfaceGraph(QObject):
+
+    def __init__(self, minimum_graph_size, maximum_graph_size):
+        super().__init__()
+
+        surfaceGraphWidget = QQuickWidget()
+        surfaceGraph = Q3DSurfaceWidgetItem()
+        surfaceGraph.setWidget(surfaceGraphWidget)
+        self._surfaceWidget = QWidget()
+        hLayout = QHBoxLayout(self._surfaceWidget)
+        surfaceGraphWidget.setMinimumSize(minimum_graph_size)
+        surfaceGraphWidget.setMaximumSize(maximum_graph_size)
+        surfaceGraphWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+        surfaceGraphWidget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+        surfaceGraphWidget.setResizeMode(QQuickWidget.ResizeMode.SizeRootObjectToView)
+        hLayout.addWidget(surfaceGraphWidget, 1)
+        vLayout = QVBoxLayout()
+        hLayout.addLayout(vLayout)
+        vLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
+        # Create control widgets
+        modelGroupBox = QGroupBox("Model")
+        sqrtSinModelRB = QRadioButton(self._surfaceWidget)
+        sqrtSinModelRB.setText("Sqrt and Sin")
+        sqrtSinModelRB.setChecked(False)
+        heightMapModelRB = QRadioButton(self._surfaceWidget)
+        heightMapModelRB.setText("Multiseries\nHeight Map")
+        heightMapModelRB.setChecked(False)
+        texturedModelRB = QRadioButton(self._surfaceWidget)
+        texturedModelRB.setText("Textured\nTopography")
+        texturedModelRB.setChecked(False)
+        modelVBox = QVBoxLayout()
+        modelVBox.addWidget(sqrtSinModelRB)
+        modelVBox.addWidget(heightMapModelRB)
+        modelVBox.addWidget(texturedModelRB)
+        modelGroupBox.setLayout(modelVBox)
+        selectionGroupBox = QGroupBox("Graph Selection Mode")
+        modeNoneRB = QRadioButton(self._surfaceWidget)
+        modeNoneRB.setText("No selection")
+        modeNoneRB.setChecked(False)
+        modeItemRB = QRadioButton(self._surfaceWidget)
+        modeItemRB.setText("Item")
+        modeItemRB.setChecked(False)
+        modeSliceRowRB = QRadioButton(self._surfaceWidget)
+        modeSliceRowRB.setText("Row Slice")
+        modeSliceRowRB.setChecked(False)
+        modeSliceColumnRB = QRadioButton(self._surfaceWidget)
+        modeSliceColumnRB.setText("Column Slice")
+        modeSliceColumnRB.setChecked(False)
+        selectionVBox = QVBoxLayout()
+        selectionVBox.addWidget(modeNoneRB)
+        selectionVBox.addWidget(modeItemRB)
+        selectionVBox.addWidget(modeSliceRowRB)
+        selectionVBox.addWidget(modeSliceColumnRB)
+        selectionGroupBox.setLayout(selectionVBox)
+        axisGroupBox = QGroupBox("Axis ranges")
+        axisMinSliderX = QSlider(Qt.Orientation.Horizontal)
+        axisMinSliderX.setMinimum(0)
+        axisMinSliderX.setTickInterval(1)
+        axisMinSliderX.setEnabled(True)
+        axisMaxSliderX = QSlider(Qt.Orientation.Horizontal)
+        axisMaxSliderX.setMinimum(1)
+        axisMaxSliderX.setTickInterval(1)
+        axisMaxSliderX.setEnabled(True)
+        axisMinSliderZ = QSlider(Qt.Orientation.Horizontal)
+        axisMinSliderZ.setMinimum(0)
+        axisMinSliderZ.setTickInterval(1)
+        axisMinSliderZ.setEnabled(True)
+        axisMaxSliderZ = QSlider(Qt.Orientation.Horizontal)
+        axisMaxSliderZ.setMinimum(1)
+        axisMaxSliderZ.setTickInterval(1)
+        axisMaxSliderZ.setEnabled(True)
+        axisVBox = QVBoxLayout(axisGroupBox)
+        axisVBox.addWidget(QLabel("Column range"))
+        axisVBox.addWidget(axisMinSliderX)
+        axisVBox.addWidget(axisMaxSliderX)
+        axisVBox.addWidget(QLabel("Row range"))
+        axisVBox.addWidget(axisMinSliderZ)
+        axisVBox.addWidget(axisMaxSliderZ)
+        # Mode-dependent controls
+        # sqrt-sin
+        colorGroupBox = QGroupBox("Custom gradient")
+
+        pixmap = gradientBtoYPB_Pixmap()
+        gradientBtoYPB = QPushButton(self._surfaceWidget)
+        gradientBtoYPB.setIcon(QIcon(pixmap))
+        gradientBtoYPB.setIconSize(pixmap.size())
+
+        pixmap = gradientGtoRPB_Pixmap()
+        gradientGtoRPB = QPushButton(self._surfaceWidget)
+        gradientGtoRPB.setIcon(QIcon(pixmap))
+        gradientGtoRPB.setIconSize(pixmap.size())
+
+        colorHBox = QHBoxLayout(colorGroupBox)
+        colorHBox.addWidget(gradientBtoYPB)
+        colorHBox.addWidget(gradientGtoRPB)
+        # Multiseries heightmap
+        showGroupBox = QGroupBox("Show Object")
+        showGroupBox.setVisible(False)
+        checkboxShowOilRigOne = QCheckBox("Oil Rig 1")
+        checkboxShowOilRigOne.setChecked(True)
+        checkboxShowOilRigTwo = QCheckBox("Oil Rig 2")
+        checkboxShowOilRigTwo.setChecked(True)
+        checkboxShowRefinery = QCheckBox("Refinery")
+        showVBox = QVBoxLayout()
+        showVBox.addWidget(checkboxShowOilRigOne)
+        showVBox.addWidget(checkboxShowOilRigTwo)
+        showVBox.addWidget(checkboxShowRefinery)
+        showGroupBox.setLayout(showVBox)
+        visualsGroupBox = QGroupBox("Visuals")
+        visualsGroupBox.setVisible(False)
+        checkboxVisualsSeeThrough = QCheckBox("See-Through")
+        checkboxHighlightOil = QCheckBox("Highlight Oil")
+        checkboxShowShadows = QCheckBox("Shadows")
+        checkboxShowShadows.setChecked(True)
+        visualVBox = QVBoxLayout(visualsGroupBox)
+        visualVBox.addWidget(checkboxVisualsSeeThrough)
+        visualVBox.addWidget(checkboxHighlightOil)
+        visualVBox.addWidget(checkboxShowShadows)
+        labelSelection = QLabel("Selection:")
+        labelSelection.setVisible(False)
+        labelSelectedItem = QLabel("Nothing")
+        labelSelectedItem.setVisible(False)
+        # Textured topography heightmap
+        enableTexture = QCheckBox("Surface texture")
+        enableTexture.setVisible(False)
+
+        label = QLabel(self._surfaceWidget)
+        label.setPixmap(highlightPixmap())
+        heightMapGroupBox = QGroupBox("Highlight color map")
+        colorMapVBox = QVBoxLayout()
+        colorMapVBox.addWidget(label)
+        heightMapGroupBox.setLayout(colorMapVBox)
+        heightMapGroupBox.setVisible(False)
+        # Populate vertical layout
+        # Common
+        vLayout.addWidget(modelGroupBox)
+        vLayout.addWidget(selectionGroupBox)
+        vLayout.addWidget(axisGroupBox)
+        # Sqrt Sin
+        vLayout.addWidget(colorGroupBox)
+        # Multiseries heightmap
+        vLayout.addWidget(showGroupBox)
+        vLayout.addWidget(visualsGroupBox)
+        vLayout.addWidget(labelSelection)
+        vLayout.addWidget(labelSelectedItem)
+        # Textured topography
+        vLayout.addWidget(heightMapGroupBox)
+        vLayout.addWidget(enableTexture)
+        # Create the controller
+        modifier = SurfaceGraphModifier(surfaceGraph, labelSelectedItem, self)
+        # Connect widget controls to controller
+        heightMapModelRB.toggled.connect(modifier.enableHeightMapModel)
+        sqrtSinModelRB.toggled.connect(modifier.enableSqrtSinModel)
+        texturedModelRB.toggled.connect(modifier.enableTopographyModel)
+        modeNoneRB.toggled.connect(modifier.toggleModeNone)
+        modeItemRB.toggled.connect(modifier.toggleModeItem)
+        modeSliceRowRB.toggled.connect(modifier.toggleModeSliceRow)
+        modeSliceColumnRB.toggled.connect(modifier.toggleModeSliceColumn)
+        axisMinSliderX.valueChanged.connect(modifier.adjustXMin)
+        axisMaxSliderX.valueChanged.connect(modifier.adjustXMax)
+        axisMinSliderZ.valueChanged.connect(modifier.adjustZMin)
+        axisMaxSliderZ.valueChanged.connect(modifier.adjustZMax)
+        # Mode dependent connections
+        gradientBtoYPB.pressed.connect(modifier.setBlackToYellowGradient)
+        gradientGtoRPB.pressed.connect(modifier.setGreenToRedGradient)
+        checkboxShowOilRigOne.toggled.connect(modifier.toggleItemOne)
+        checkboxShowOilRigTwo.toggled.connect(modifier.toggleItemTwo)
+        checkboxShowRefinery.toggled.connect(modifier.toggleItemThree)
+        checkboxVisualsSeeThrough.toggled.connect(modifier.toggleSeeThrough)
+        checkboxHighlightOil.toggled.connect(modifier.toggleOilHighlight)
+        checkboxShowShadows.toggled.connect(modifier.toggleShadows)
+        enableTexture.toggled.connect(modifier.toggleSurfaceTexture)
+        # Connections to disable features depending on mode
+        sqrtSinModelRB.toggled.connect(colorGroupBox.setVisible)
+        heightMapModelRB.toggled.connect(showGroupBox.setVisible)
+        heightMapModelRB.toggled.connect(visualsGroupBox.setVisible)
+        heightMapModelRB.toggled.connect(labelSelection.setVisible)
+        heightMapModelRB.toggled.connect(labelSelectedItem.setVisible)
+        texturedModelRB.toggled.connect(enableTexture.setVisible)
+        texturedModelRB.toggled.connect(heightMapGroupBox.setVisible)
+        modifier.setAxisMinSliderX(axisMinSliderX)
+        modifier.setAxisMaxSliderX(axisMaxSliderX)
+        modifier.setAxisMinSliderZ(axisMinSliderZ)
+        modifier.setAxisMaxSliderZ(axisMaxSliderZ)
+        sqrtSinModelRB.setChecked(True)
+        modeItemRB.setChecked(True)
+        enableTexture.setChecked(True)
+
+    def surfaceWidget(self):
+        return self._surfaceWidget
diff --git a/examples/graphs/3d/widgetgraphgallery/surfacegraphmodifier.py b/examples/graphs/3d/widgetgraphgallery/surfacegraphmodifier.py
new file mode 100644 (file)
index 0000000..d9b2126
--- /dev/null
@@ -0,0 +1,762 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+import os
+from enum import Enum
+from math import sqrt, sin, cos, degrees
+from pathlib import Path
+
+from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Slot
+from PySide6.QtGui import (QColor, QFont, QImage, QLinearGradient,
+                           QQuaternion, QVector2D, QVector3D, QWheelEvent)
+from PySide6.QtGraphs import (QCustom3DItem,
+                              QCustom3DLabel, QHeightMapSurfaceDataProxy,
+                              QValue3DAxis, QSurfaceDataItem,
+                              QSurfaceDataProxy, QSurface3DSeries,
+                              QtGraphs3D, QGraphsTheme)
+
+
+from highlightseries import HighlightSeries
+from topographicseries import TopographicSeries
+
+
+class InputState(Enum):
+    StateNormal = 0
+    StateDraggingX = 1
+    StateDraggingZ = 2
+    StateDraggingY = 3
+
+
+SAMPLE_COUNT_X = 150
+SAMPLE_COUNT_Z = 150
+HEIGHTMAP_GRID_STEP_X = 6
+HEIGHTMAP_GRID_STEP_Z = 6
+SAMPLE_MIN = -8.0
+SAMPLE_MAX = 8.0
+SPEED_MODIFIER = 20.0
+
+AREA_WIDTH = 8000.0
+AREA_HEIGHT = 8000.0
+ASPECT_RATIO = 0.1389
+MIN_RANGE = AREA_WIDTH * 0.49
+
+
+class SurfaceGraphModifier(QObject):
+
+    def __init__(self, surface, label, parent):
+        super().__init__(parent)
+        self._state = InputState.StateNormal
+        self._data_path = Path(__file__).resolve().parent / "data"
+        self._graph = surface
+        self._textField = label
+        self._sqrtSinProxy = None
+        self._sqrtSinSeries = None
+        self._heightMapProxyOne = None
+        self._heightMapProxyTwo = None
+        self._heightMapProxyThree = None
+        self._heightMapSeriesOne = None
+        self._heightMapSeriesTwo = None
+        self._heightMapSeriesThree = None
+
+        self._axisMinSliderX = None
+        self._axisMaxSliderX = None
+        self._axisMinSliderZ = None
+        self._axisMaxSliderZ = None
+        self._rangeMinX = 0.0
+        self._rangeMinZ = 0.0
+        self._stepX = 0.0
+        self._stepZ = 0.0
+        self._heightMapWidth = 0
+        self._heightMapHeight = 0
+
+        self._axisXMinValue = 0.0
+        self._axisXMaxValue = 0.0
+        self._axisXMinRange = 0.0
+        self._axisZMinValue = 0.0
+        self._axisZMaxValue = 0.0
+        self._axisZMinRange = 0.0
+        self._areaMinValue = 0.0
+        self._areaMaxValue = 0.0
+
+        self._selectionAnimation = None
+        self._titleLabel = None
+        self._previouslyAnimatedItem = None
+        self._previousScaling = {}
+
+        self._topography = None
+        self._highlight = None
+        self._highlightWidth = 0
+        self._highlightHeight = 0
+
+        self._graph.setCameraZoomLevel(85.0)
+        self._graph.setCameraPreset(QtGraphs3D.CameraPreset.IsometricRight)
+        theme = self._graph.activeTheme()
+        theme.setTheme(QGraphsTheme.Theme.MixSeries)
+        theme.setLabelBackgroundVisible(False)
+        theme.setLabelBorderVisible(False)
+
+        self._x_axis = QValue3DAxis()
+        self._y_axis = QValue3DAxis()
+        self._z_axis = QValue3DAxis()
+        self._graph.setAxisX(self._x_axis)
+        self._graph.setAxisY(self._y_axis)
+        self._graph.setAxisZ(self._z_axis)
+
+        #
+        # Sqrt Sin
+        #
+        self._sqrtSinProxy = QSurfaceDataProxy()
+        self._sqrtSinSeries = QSurface3DSeries(self._sqrtSinProxy)
+        self.fillSqrtSinProxy()
+
+        #
+        # Multisurface heightmap
+        #
+        # Create the first surface layer
+        heightMapImageOne = QImage(self._data_path / "layer_1.png")
+        self._heightMapProxyOne = QHeightMapSurfaceDataProxy(heightMapImageOne)
+        self._heightMapSeriesOne = QSurface3DSeries(self._heightMapProxyOne)
+        self._heightMapSeriesOne.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+        self._heightMapProxyOne.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+        # Create the other 2 surface layers
+        heightMapImageTwo = QImage(self._data_path / "layer_2.png")
+        self._heightMapProxyTwo = QHeightMapSurfaceDataProxy(heightMapImageTwo)
+        self._heightMapSeriesTwo = QSurface3DSeries(self._heightMapProxyTwo)
+        self._heightMapSeriesTwo.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+        self._heightMapProxyTwo.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+        heightMapImageThree = QImage(self._data_path / "layer_3.png")
+        self._heightMapProxyThree = QHeightMapSurfaceDataProxy(heightMapImageThree)
+        self._heightMapSeriesThree = QSurface3DSeries(self._heightMapProxyThree)
+        self._heightMapSeriesThree.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+        self._heightMapProxyThree.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+        # The images are the same size, so it's enough to get the dimensions
+        # from one
+        self._heightMapWidth = heightMapImageOne.width()
+        self._heightMapHeight = heightMapImageOne.height()
+
+        # Set the gradients for multi-surface layers
+        grOne = QLinearGradient()
+        grOne.setColorAt(0.0, Qt.GlobalColor.black)
+        grOne.setColorAt(0.38, Qt.GlobalColor.darkYellow)
+        grOne.setColorAt(0.39, Qt.GlobalColor.darkGreen)
+        grOne.setColorAt(0.5, Qt.GlobalColor.darkGray)
+        grOne.setColorAt(1.0, Qt.GlobalColor.gray)
+        self._heightMapSeriesOne.setBaseGradient(grOne)
+        self._heightMapSeriesOne.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+        grTwo = QLinearGradient()
+        grTwo.setColorAt(0.39, Qt.GlobalColor.blue)
+        grTwo.setColorAt(0.4, Qt.GlobalColor.white)
+        self._heightMapSeriesTwo.setBaseGradient(grTwo)
+        self._heightMapSeriesTwo.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+        grThree = QLinearGradient()
+        grThree.setColorAt(0.0, Qt.GlobalColor.white)
+        grThree.setColorAt(0.05, Qt.GlobalColor.black)
+        self._heightMapSeriesThree.setBaseGradient(grThree)
+        self._heightMapSeriesThree.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+        # Custom items and label
+        self._graph.selectedElementChanged.connect(self.handleElementSelected)
+
+        self._selectionAnimation = QPropertyAnimation(self)
+        self._selectionAnimation.setPropertyName(b"scaling")
+        self._selectionAnimation.setDuration(500)
+        self._selectionAnimation.setLoopCount(-1)
+
+        titleFont = QFont("Century Gothic", 30)
+        titleFont.setBold(True)
+        self._titleLabel = QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
+                                          QVector3D(0.0, 1.2, 0.0),
+                                          QVector3D(1.0, 1.0, 0.0),
+                                          QQuaternion())
+        self._titleLabel.setPositionAbsolute(True)
+        self._titleLabel.setFacingCamera(True)
+        self._titleLabel.setBackgroundColor(QColor(0x66cdaa))
+        self._graph.addCustomItem(self._titleLabel)
+        self._titleLabel.setVisible(False)
+
+        # Make two of the custom object visible
+        self.toggleItemOne(True)
+        self.toggleItemTwo(True)
+
+        #
+        # Topographic map
+        #
+        self._topography = TopographicSeries()
+        file_name = os.fspath(self._data_path / "topography.png")
+        self._topography.setTopographyFile(file_name, AREA_WIDTH, AREA_HEIGHT)
+        self._topography.setItemLabelFormat("@yLabel m")
+
+        self._highlight = HighlightSeries()
+        self._highlight.setTopographicSeries(self._topography)
+        self._highlight.setMinHeight(MIN_RANGE * ASPECT_RATIO)
+        self._highlight.handleGradientChange(AREA_WIDTH * ASPECT_RATIO)
+        self._graph.axisY().maxChanged.connect(self._highlight.handleGradientChange)
+
+        self._graph.wheel.connect(self.onWheel)
+        self._graph.dragged.connect(self.handleAxisDragging)
+
+    def fillSqrtSinProxy(self):
+        stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+        stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+
+        dataArray = []
+        for i in range(0, SAMPLE_COUNT_Z):
+            newRow = []
+            # Keep values within range bounds, since just adding step can
+            # cause minor drift due to the rounding errors.
+            z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
+            for j in range(0, SAMPLE_COUNT_X):
+                x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
+                R = sqrt(z * z + x * x) + 0.01
+                y = (sin(R) / R + 0.24) * 1.61
+                item = QSurfaceDataItem(QVector3D(x, y, z))
+                newRow.append(item)
+            dataArray.append(newRow)
+        self._sqrtSinProxy.resetArray(dataArray)
+
+    @Slot(bool)
+    def enableSqrtSinModel(self, enable):
+        if enable:
+            self._sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurfaceAndWireframe)
+            self._sqrtSinSeries.setShading(QSurface3DSeries.Shading.Flat)
+
+            self._graph.axisX().setLabelFormat("%.2f")
+            self._graph.axisZ().setLabelFormat("%.2f")
+            self._graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
+            self._graph.axisY().setRange(0.0, 2.0)
+            self._graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
+            self._graph.axisX().setLabelAutoAngle(30.0)
+            self._graph.axisY().setLabelAutoAngle(90.0)
+            self._graph.axisZ().setLabelAutoAngle(30.0)
+
+            self._graph.removeSeries(self._heightMapSeriesOne)
+            self._graph.removeSeries(self._heightMapSeriesTwo)
+            self._graph.removeSeries(self._heightMapSeriesThree)
+            self._graph.removeSeries(self._topography)
+            self._graph.removeSeries(self._highlight)
+
+            self._graph.addSeries(self._sqrtSinSeries)
+
+            self._titleLabel.setVisible(False)
+            self._graph.axisX().setTitleVisible(False)
+            self._graph.axisY().setTitleVisible(False)
+            self._graph.axisZ().setTitleVisible(False)
+
+            self._graph.axisX().setTitle("")
+            self._graph.axisY().setTitle("")
+            self._graph.axisZ().setTitle("")
+
+            # Reset range sliders for Sqrt & Sin
+            self._rangeMinX = SAMPLE_MIN
+            self._rangeMinZ = SAMPLE_MIN
+            self._stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+            self._stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+            self._axisMinSliderX.setMinimum(0)
+            self._axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
+            self._axisMinSliderX.setValue(0)
+            self._axisMaxSliderX.setMinimum(1)
+            self._axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
+            self._axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
+            self._axisMinSliderZ.setMinimum(0)
+            self._axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
+            self._axisMinSliderZ.setValue(0)
+            self._axisMaxSliderZ.setMinimum(1)
+            self._axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
+            self._axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
+
+            self._graph.setZoomEnabled(True)
+
+    @Slot(bool)
+    def enableHeightMapModel(self, enable):
+        if enable:
+            self._heightMapSeriesOne.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+            self._heightMapSeriesOne.setShading(QSurface3DSeries.Shading.Flat)
+            self._heightMapSeriesTwo.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+            self._heightMapSeriesTwo.setShading(QSurface3DSeries.Shading.Flat)
+            self._heightMapSeriesThree.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+            self._heightMapSeriesThree.setShading(QSurface3DSeries.Shading.Flat)
+
+            self._graph.axisX().setLabelFormat("%.1f N")
+            self._graph.axisZ().setLabelFormat("%.1f E")
+            self._graph.axisX().setRange(34.0, 40.0)
+            self._graph.axisY().setAutoAdjustRange(True)
+            self._graph.axisZ().setRange(18.0, 24.0)
+
+            self._graph.axisX().setTitle("Latitude")
+            self._graph.axisY().setTitle("Height")
+            self._graph.axisZ().setTitle("Longitude")
+
+            self._graph.removeSeries(self._sqrtSinSeries)
+            self._graph.removeSeries(self._topography)
+            self._graph.removeSeries(self._highlight)
+            self._graph.addSeries(self._heightMapSeriesOne)
+            self._graph.addSeries(self._heightMapSeriesTwo)
+            self._graph.addSeries(self._heightMapSeriesThree)
+
+            self._titleLabel.setVisible(True)
+            self._graph.axisX().setTitleVisible(True)
+            self._graph.axisY().setTitleVisible(True)
+            self._graph.axisZ().setTitleVisible(True)
+
+            # Reset range sliders for height map
+            mapGridCountX = self._heightMapWidth / HEIGHTMAP_GRID_STEP_X
+            mapGridCountZ = self._heightMapHeight / HEIGHTMAP_GRID_STEP_Z
+            self._rangeMinX = 34.0
+            self._rangeMinZ = 18.0
+            self._stepX = 6.0 / float(mapGridCountX - 1)
+            self._stepZ = 6.0 / float(mapGridCountZ - 1)
+            self._axisMinSliderX.setMinimum(0)
+            self._axisMinSliderX.setMaximum(mapGridCountX - 2)
+            self._axisMinSliderX.setValue(0)
+            self._axisMaxSliderX.setMinimum(1)
+            self._axisMaxSliderX.setMaximum(mapGridCountX - 1)
+            self._axisMaxSliderX.setValue(mapGridCountX - 1)
+            self._axisMinSliderZ.setMinimum(0)
+            self._axisMinSliderZ.setMaximum(mapGridCountZ - 2)
+            self._axisMinSliderZ.setValue(0)
+            self._axisMaxSliderZ.setMinimum(1)
+            self._axisMaxSliderZ.setMaximum(mapGridCountZ - 1)
+            self._axisMaxSliderZ.setValue(mapGridCountZ - 1)
+
+            self._graph.wheel.disconnect(self.onWheel)
+            self._graph.dragged.disconnect(self.handleAxisDragging)
+            self._graph.setDefaultInputHandler()
+            self._graph.setZoomEnabled(True)
+
+    @Slot(bool)
+    def enableTopographyModel(self, enable):
+        if enable:
+            self._graph.axisX().setLabelFormat("%i")
+            self._graph.axisZ().setLabelFormat("%i")
+            self._graph.axisX().setRange(0.0, AREA_WIDTH)
+            self._graph.axisY().setRange(100.0, AREA_WIDTH * ASPECT_RATIO)
+            self._graph.axisZ().setRange(0.0, AREA_HEIGHT)
+            self._graph.axisX().setLabelAutoAngle(30.0)
+            self._graph.axisY().setLabelAutoAngle(90.0)
+            self._graph.axisZ().setLabelAutoAngle(30.0)
+
+            self._graph.removeSeries(self._heightMapSeriesOne)
+            self._graph.removeSeries(self._heightMapSeriesTwo)
+            self._graph.removeSeries(self._heightMapSeriesThree)
+            self._graph.addSeries(self._topography)
+            self._graph.addSeries(self._highlight)
+
+            self._titleLabel.setVisible(False)
+            self._graph.axisX().setTitleVisible(False)
+            self._graph.axisY().setTitleVisible(False)
+            self._graph.axisZ().setTitleVisible(False)
+
+            self._graph.axisX().setTitle("")
+            self._graph.axisY().setTitle("")
+            self._graph.axisZ().setTitle("")
+
+            # Reset range sliders for topography map
+            self._rangeMinX = 0.0
+            self._rangeMinZ = 0.0
+            self._stepX = 1.0
+            self._stepZ = 1.0
+            self._axisMinSliderX.setMinimum(0)
+            self._axisMinSliderX.setMaximum(AREA_WIDTH - 200)
+            self._axisMinSliderX.setValue(0)
+            self._axisMaxSliderX.setMinimum(200)
+            self._axisMaxSliderX.setMaximum(AREA_WIDTH)
+            self._axisMaxSliderX.setValue(AREA_WIDTH)
+            self._axisMinSliderZ.setMinimum(0)
+            self._axisMinSliderZ.setMaximum(AREA_HEIGHT - 200)
+            self._axisMinSliderZ.setValue(0)
+            self._axisMaxSliderZ.setMinimum(200)
+            self._axisMaxSliderZ.setMaximum(AREA_HEIGHT)
+            self._axisMaxSliderZ.setValue(AREA_HEIGHT)
+
+            self._areaMinValue = 0
+            self._areaMaxValue = AREA_WIDTH
+            self._axisXMinValue = self._areaMinValue
+            self._axisXMaxValue = self._areaMaxValue
+            self._axisZMinValue = self._areaMinValue
+            self._axisZMaxValue = self._areaMaxValue
+            self._axisXMinRange = MIN_RANGE
+            self._axisZMinRange = MIN_RANGE
+
+            self._graph.wheel.connect(self.onWheel)
+            self._graph.dragged.connect(self.handleAxisDragging)
+            self._graph.setZoomEnabled(False)
+
+    def adjustXMin(self, min):
+        minX = self._stepX * float(min) + self._rangeMinX
+
+        max = self._axisMaxSliderX.value()
+        if min >= max:
+            max = min + 1
+            self._axisMaxSliderX.setValue(max)
+
+        maxX = self._stepX * max + self._rangeMinX
+
+        self.setAxisXRange(minX, maxX)
+
+    def adjustXMax(self, max):
+        maxX = self._stepX * float(max) + self._rangeMinX
+
+        min = self._axisMinSliderX.value()
+        if max <= min:
+            min = max - 1
+            self._axisMinSliderX.setValue(min)
+
+        minX = self._stepX * min + self._rangeMinX
+
+        self.setAxisXRange(minX, maxX)
+
+    def adjustZMin(self, min):
+        minZ = self._stepZ * float(min) + self._rangeMinZ
+
+        max = self._axisMaxSliderZ.value()
+        if min >= max:
+            max = min + 1
+            self._axisMaxSliderZ.setValue(max)
+
+        maxZ = self._stepZ * max + self._rangeMinZ
+
+        self.setAxisZRange(minZ, maxZ)
+
+    def adjustZMax(self, max):
+        maxX = self._stepZ * float(max) + self._rangeMinZ
+
+        min = self._axisMinSliderZ.value()
+        if max <= min:
+            min = max - 1
+            self._axisMinSliderZ.setValue(min)
+
+        minX = self._stepZ * min + self._rangeMinZ
+
+        self.setAxisZRange(minX, maxX)
+
+    def setAxisXRange(self, min, max):
+        self._graph.axisX().setRange(min, max)
+
+    def setAxisZRange(self, min, max):
+        self._graph.axisZ().setRange(min, max)
+
+    def setBlackToYellowGradient(self):
+        gr = QLinearGradient()
+        gr.setColorAt(0.0, Qt.GlobalColor.black)
+        gr.setColorAt(0.33, Qt.blue)
+        gr.setColorAt(0.67, Qt.red)
+        gr.setColorAt(1.0, Qt.yellow)
+
+        self._sqrtSinSeries.setBaseGradient(gr)
+        self._sqrtSinSeries.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+    def setGreenToRedGradient(self):
+        gr = QLinearGradient()
+        gr.setColorAt(0.0, Qt.darkGreen)
+        gr.setColorAt(0.5, Qt.yellow)
+        gr.setColorAt(0.8, Qt.red)
+        gr.setColorAt(1.0, Qt.darkRed)
+
+        self._sqrtSinSeries.setBaseGradient(gr)
+        self._sqrtSinSeries.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+    @Slot(bool)
+    def toggleItemOne(self, show):
+        positionOne = QVector3D(39.0, 77.0, 19.2)
+        positionOnePipe = QVector3D(39.0, 45.0, 19.2)
+        positionOneLabel = QVector3D(39.0, 107.0, 19.2)
+        if show:
+            color = QImage(2, 2, QImage.Format.Format_RGB32)
+            color.fill(Qt.GlobalColor.red)
+            file_name = os.fspath(self._data_path / "oilrig.mesh")
+            item = QCustom3DItem(file_name, positionOne,
+                                 QVector3D(0.025, 0.025, 0.025),
+                                 QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 45.0),
+                                 color)
+            self._graph.addCustomItem(item)
+            file_name = os.fspath(self._data_path / "pipe.mesh")
+            item = QCustom3DItem(file_name, positionOnePipe,
+                                 QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+                                 color)
+            item.setShadowCasting(False)
+            self._graph.addCustomItem(item)
+
+            label = QCustom3DLabel()
+            label.setText("Oil Rig One")
+            label.setPosition(positionOneLabel)
+            label.setScaling(QVector3D(1.0, 1.0, 1.0))
+            self._graph.addCustomItem(label)
+        else:
+            self.resetSelection()
+            self._graph.removeCustomItemAt(positionOne)
+            self._graph.removeCustomItemAt(positionOnePipe)
+            self._graph.removeCustomItemAt(positionOneLabel)
+
+    @Slot(bool)
+    def toggleItemTwo(self, show):
+        positionTwo = QVector3D(34.5, 77.0, 23.4)
+        positionTwoPipe = QVector3D(34.5, 45.0, 23.4)
+        positionTwoLabel = QVector3D(34.5, 107.0, 23.4)
+        if show:
+            color = QImage(2, 2, QImage.Format.Format_RGB32)
+            color.fill(Qt.GlobalColor.red)
+            item = QCustom3DItem()
+            file_name = os.fspath(self._data_path / "oilrig.mesh")
+            item.setMeshFile(file_name)
+            item.setPosition(positionTwo)
+            item.setScaling(QVector3D(0.025, 0.025, 0.025))
+            item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 25.0))
+            item.setTextureImage(color)
+            self._graph.addCustomItem(item)
+            file_name = os.fspath(self._data_path / "pipe.mesh")
+            item = QCustom3DItem(file_name, positionTwoPipe,
+                                 QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+                                 color)
+            item.setShadowCasting(False)
+            self._graph.addCustomItem(item)
+
+            label = QCustom3DLabel()
+            label.setText("Oil Rig Two")
+            label.setPosition(positionTwoLabel)
+            label.setScaling(QVector3D(1.0, 1.0, 1.0))
+            self._graph.addCustomItem(label)
+        else:
+            self.resetSelection()
+            self._graph.removeCustomItemAt(positionTwo)
+            self._graph.removeCustomItemAt(positionTwoPipe)
+            self._graph.removeCustomItemAt(positionTwoLabel)
+
+    @Slot(bool)
+    def toggleItemThree(self, show):
+        positionThree = QVector3D(34.5, 86.0, 19.1)
+        positionThreeLabel = QVector3D(34.5, 116.0, 19.1)
+        if show:
+            color = QImage(2, 2, QImage.Format.Format_RGB32)
+            color.fill(Qt.darkMagenta)
+            item = QCustom3DItem()
+            file_name = os.fspath(self._data_path / "refinery.mesh")
+            item.setMeshFile(file_name)
+            item.setPosition(positionThree)
+            item.setScaling(QVector3D(0.04, 0.04, 0.04))
+            item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 75.0))
+            item.setTextureImage(color)
+            self._graph.addCustomItem(item)
+
+            label = QCustom3DLabel()
+            label.setText("Refinery")
+            label.setPosition(positionThreeLabel)
+            label.setScaling(QVector3D(1.0, 1.0, 1.0))
+            self._graph.addCustomItem(label)
+        else:
+            self.resetSelection()
+            self._graph.removeCustomItemAt(positionThree)
+            self._graph.removeCustomItemAt(positionThreeLabel)
+
+    @Slot(bool)
+    def toggleSeeThrough(self, seethrough):
+        s0 = self._graph.seriesList()[0]
+        s1 = self._graph.seriesList()[1]
+        if seethrough:
+            s0.setDrawMode(QSurface3DSeries.DrawWireframe)
+            s1.setDrawMode(QSurface3DSeries.DrawWireframe)
+        else:
+            s0.setDrawMode(QSurface3DSeries.DrawSurface)
+            s1.setDrawMode(QSurface3DSeries.DrawSurface)
+
+    @Slot(bool)
+    def toggleOilHighlight(self, highlight):
+        s2 = self._graph.seriesList()[2]
+        if highlight:
+            grThree = QLinearGradient()
+            grThree.setColorAt(0.0, Qt.GlobalColor.black)
+            grThree.setColorAt(0.05, Qt.red)
+            s2.setBaseGradient(grThree)
+        else:
+            grThree = QLinearGradient()
+            grThree.setColorAt(0.0, Qt.GlobalColor.white)
+            grThree.setColorAt(0.05, Qt.GlobalColor.black)
+            s2.setBaseGradient(grThree)
+
+    @Slot(bool)
+    def toggleShadows(self, shadows):
+        sq = (QtGraphs3D.ShadowQuality.Medium
+              if shadows else QtGraphs3D.ShadowQuality.None_)
+        self._graph.setShadowQuality(sq)
+
+    @Slot(bool)
+    def toggleSurfaceTexture(self, enable):
+        if enable:
+            file_name = os.fspath(self._data_path / "maptexture.jpg")
+            self._topography.setTextureFile(file_name)
+        else:
+            self._topography.setTextureFile("")
+
+    def handleElementSelected(self, type):
+        self.resetSelection()
+        if type == QtGraphs3D.ElementType.CustomItem:
+            item = self._graph.selectedCustomItem()
+            text = ""
+            if isinstance(item, QCustom3DItem):
+                text += "Custom label: "
+            else:
+                file = item.meshFile().split("/")[-1]
+                text += f"{file}: "
+
+            text += str(self._graph.selectedCustomItemIndex())
+            self._textField.setText(text)
+            self._previouslyAnimatedItem = item
+            self._previousScaling = item.scaling()
+            self._selectionAnimation.setTargetObject(item)
+            self._selectionAnimation.setStartValue(item.scaling())
+            self._selectionAnimation.setEndValue(item.scaling() * 1.5)
+            self._selectionAnimation.start()
+        elif type == QtGraphs3D.ElementType.Series:
+            text = "Surface ("
+            series = self._graph.selectedSeries()
+            if series:
+                point = series.selectedPoint()
+                text += f"{point.x()}, {point.y()}"
+            text += ")"
+            self._textField.setText(text)
+        elif (type.value > QtGraphs3D.ElementType.Series.value
+              and type.value < QtGraphs3D.ElementType.CustomItem.value):
+            index = self._graph.selectedLabelIndex()
+            text = ""
+            if type == QtGraphs3D.ElementType.AxisXLabel:
+                text += "Axis X label: "
+                self._state = InputState.StateDraggingX
+            elif type == QtGraphs3D.ElementType.AxisYLabel:
+                text += "Axis Y label: "
+                self._state = InputState.StateDraggingY
+            else:
+                text += "Axis Z label: "
+                self._state = InputState.StateDraggingZ
+            text += str(index)
+            self._textField.setText(text)
+        else:
+            self._textField.setText("Nothing")
+
+    def resetSelection(self):
+        self._selectionAnimation.stop()
+        if self._previouslyAnimatedItem:
+            self._previouslyAnimatedItem.setScaling(self._previousScaling)
+        self._previouslyAnimatedItem = None
+
+    def toggleModeNone(self):
+        self._graph.setSelectionMode(QtGraphs3D.SelectionFlag.None_)
+
+    def toggleModeItem(self):
+        self._graph.setSelectionMode(QtGraphs3D.SelectionFlag.Item)
+
+    def toggleModeSliceRow(self):
+        sm = (QtGraphs3D.SelectionFlag.ItemAndRow
+              | QtGraphs3D.SelectionFlag.Slice
+              | QtGraphs3D.SelectionFlag.MultiSeries)
+        self._graph.setSelectionMode(sm)
+
+    def toggleModeSliceColumn(self):
+        sm = (QtGraphs3D.SelectionFlag.ItemAndColumn
+              | QtGraphs3D.SelectionFlag.Slice
+              | QtGraphs3D.SelectionFlag.MultiSeries)
+        self._graph.setSelectionMode(sm)
+
+    def setAxisMinSliderX(self, slider):
+        self._axisMinSliderX = slider
+
+    def setAxisMaxSliderX(self, slider):
+        self._axisMaxSliderX = slider
+
+    def setAxisMinSliderZ(self, slider):
+        self._axisMinSliderZ = slider
+
+    def setAxisMaxSliderZ(self, slider):
+        self._axisMaxSliderZ = slider
+
+    def checkConstraints(self):
+        if self._axisXMinValue < self._areaMinValue:
+            self._axisXMinValue = self._areaMinValue
+        if self._axisXMaxValue > self._areaMaxValue:
+            self._axisXMaxValue = self._areaMaxValue
+        # Don't allow too much zoom in
+        range = self._axisXMaxValue - self._axisXMinValue
+        if range < self._axisXMinRange:
+            adjust = (self._axisXMinRange - range) / 2.0
+            self._axisXMinValue -= adjust
+            self._axisXMaxValue += adjust
+
+        if self._axisZMinValue < self._areaMinValue:
+            self._axisZMinValue = self._areaMinValue
+        if self._axisZMaxValue > self._areaMaxValue:
+            self._axisZMaxValue = self._areaMaxValue
+        # Don't allow too much zoom in
+        range = self._axisZMaxValue - self._axisZMinValue
+        if range < self._axisZMinRange:
+            adjust = (self._axisZMinRange - range) / 2.0
+            self._axisZMinValue -= adjust
+            self._axisZMaxValue += adjust
+
+    @Slot(QVector2D)
+    def handleAxisDragging(self, delta):
+
+        distance = float(0)
+
+        # Get scene orientation from active camera
+        xRotation = self._graph.cameraXRotation()
+
+        # Calculate directional drag multipliers based on rotation
+        xMulX = cos(degrees(xRotation))
+        xMulY = sin(degrees(xRotation))
+        zMulX = sin(degrees(xRotation))
+        zMulY = cos(degrees(xRotation))
+
+        # Get the drag amount
+        move = delta.toPoint()
+
+        # Adjust axes
+        if self._state == InputState.StateDraggingX:
+            distance = (move.x() * xMulX - move.y() * xMulY) * SPEED_MODIFIER
+            self._axisXMinValue -= distance
+            self._axisXMaxValue -= distance
+            if self._axisXMinValue < self._areaMinValue:
+                dist = self._axisXMaxValue - self._axisXMinValue
+                self._axisXMinValue = self._areaMinValue
+                self._axisXMaxValue = self._axisXMinValue + dist
+
+            if self._axisXMaxValue > self._areaMaxValue:
+                dist = self._axisXMaxValue - self._axisXMinValue
+                self._axisXMaxValue = self._areaMaxValue
+                self._axisXMinValue = self._axisXMaxValue - dist
+
+            self._graph.axisX().setRange(self._axisXMinValue, self._axisXMaxValue)
+        elif self._state == InputState.StateDraggingZ:
+            distance = (move.x() * zMulX + move.y() * zMulY) * SPEED_MODIFIER
+            self._axisZMinValue += distance
+            self._axisZMaxValue += distance
+            if self._axisZMinValue < self._areaMinValue:
+                dist = self._axisZMaxValue - self._axisZMinValue
+                self._axisZMinValue = self._areaMinValue
+                self._axisZMaxValue = self._axisZMinValue + dist
+
+            if self._axisZMaxValue > self._areaMaxValue:
+                dist = self._axisZMaxValue - self._axisZMinValue
+                self._axisZMaxValue = self._areaMaxValue
+                self._axisZMinValue = self._axisZMaxValue - dist
+
+            self._graph.axisZ().setRange(self._axisZMinValue, self._axisZMaxValue)
+
+    @Slot(QWheelEvent)
+    def onWheel(self, event):
+        delta = float(event.angleDelta().y())
+
+        self._axisXMinValue += delta
+        self._axisXMaxValue -= delta
+        self._axisZMinValue += delta
+        self._axisZMaxValue -= delta
+        self.checkConstraints()
+
+        y = (self._axisXMaxValue - self._axisXMinValue) * ASPECT_RATIO
+
+        self._graph.axisX().setRange(self._axisXMinValue, self._axisXMaxValue)
+        self._graph.axisY().setRange(100.0, y)
+        self._graph.axisZ().setRange(self._axisZMinValue, self._axisZMaxValue)
diff --git a/examples/graphs/3d/widgetgraphgallery/topographicseries.py b/examples/graphs/3d/widgetgraphgallery/topographicseries.py
new file mode 100644 (file)
index 0000000..41cc35a
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import Qt
+from PySide6.QtGui import QImage, QVector3D
+from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem)
+
+
+# Value used to encode height data as RGB value on PNG file
+PACKING_FACTOR = 11983.0
+
+
+class TopographicSeries(QSurface3DSeries):
+
+    def __init__(self):
+        super().__init__()
+        self._sampleCountX = 0.0
+        self._sampleCountZ = 0.0
+        self.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+        self.setShading(QSurface3DSeries.Shading.Flat)
+        self.setBaseColor(Qt.GlobalColor.white)
+
+    def sampleCountX(self):
+        return self._sampleCountX
+
+    def sampleCountZ(self):
+        return self._sampleCountZ
+
+    def setTopographyFile(self, file, width, height):
+        heightMapImage = QImage(file)
+        bits = heightMapImage.bits()
+        imageHeight = heightMapImage.height()
+        imageWidth = heightMapImage.width()
+        widthBits = imageWidth * 4
+        stepX = width / float(imageWidth)
+        stepZ = height / float(imageHeight)
+
+        dataArray = []
+        for i in range(0, imageHeight):
+            p = i * widthBits
+            z = height - float(i) * stepZ
+            newRow = []
+            for j in range(0, imageWidth):
+                aa = bits[p + 0]
+                rr = bits[p + 1]
+                gg = bits[p + 2]
+                color = (gg << 16) + (rr << 8) + aa
+                y = float(color) / PACKING_FACTOR
+                item = QSurfaceDataItem(QVector3D(float(j) * stepX, y, z))
+                newRow.append(item)
+                p += 4
+            dataArray.append(newRow)
+
+        self.dataProxy().resetArray(dataArray)
+
+        self._sampleCountX = float(imageWidth)
+        self._sampleCountZ = float(imageHeight)
diff --git a/examples/graphs/3d/widgetgraphgallery/variantbardatamapping.py b/examples/graphs/3d/widgetgraphgallery/variantbardatamapping.py
new file mode 100644 (file)
index 0000000..5b1986b
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QObject, Signal
+
+
+class VariantBarDataMapping(QObject):
+
+    rowIndexChanged = Signal()
+    columnIndexChanged = Signal()
+    valueIndexChanged = Signal()
+    rowCategoriesChanged = Signal()
+    columnCategoriesChanged = Signal()
+    mappingChanged = Signal()
+
+    def __init__(self, rowIndex, columnIndex, valueIndex,
+                 rowCategories=[], columnCategories=[]):
+        super().__init__(None)
+        self._rowIndex = rowIndex
+        self._columnIndex = columnIndex
+        self._valueIndex = valueIndex
+        self._rowCategories = rowCategories
+        self._columnCategories = columnCategories
+
+    def setRowIndex(self, index):
+        self._rowIndex = index
+        self.mappingChanged.emit()
+
+    def rowIndex(self):
+        return self._rowIndex
+
+    def setColumnIndex(self, index):
+        self._columnIndex = index
+        self.mappingChanged.emit()
+
+    def columnIndex(self):
+        return self._columnIndex
+
+    def setValueIndex(self, index):
+        self._valueIndex = index
+        self.mappingChanged.emit()
+
+    def valueIndex(self):
+        return self._valueIndex
+
+    def setRowCategories(self, categories):
+        self._rowCategories = categories
+        self.mappingChanged.emit()
+
+    def rowCategories(self):
+        return self._rowCategories
+
+    def setColumnCategories(self, categories):
+        self._columnCategories = categories
+        self.mappingChanged.emit()
+
+    def columnCategories(self):
+        return self._columnCategories
+
+    def remap(self, rowIndex, columnIndex, valueIndex,
+              rowCategories=[], columnCategories=[]):
+        self._rowIndex = rowIndex
+        self._columnIndex = columnIndex
+        self._valueIndex = valueIndex
+        self._rowCategories = rowCategories
+        self._columnCategories = columnCategories
+        self.mappingChanged.emit()
diff --git a/examples/graphs/3d/widgetgraphgallery/variantbardataproxy.py b/examples/graphs/3d/widgetgraphgallery/variantbardataproxy.py
new file mode 100644 (file)
index 0000000..9cd71b1
--- /dev/null
@@ -0,0 +1,101 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import Slot
+from PySide6.QtGraphs import QBarDataProxy, QBarDataItem
+
+
+class VariantBarDataProxy(QBarDataProxy):
+
+    def __init__(self):
+        super().__init__()
+        self._dataSet = None
+        self._mapping = None
+
+    def setDataSet(self, newSet):
+        if self._dataSet:
+            self._dataSet.itemsAdded.disconnect(self.handleItemsAdded)
+            self._dataSet.dataCleared.disconnect(self.handleDataCleared)
+
+        self._dataSet = newSet
+
+        if self._dataSet:
+            self._dataSet.itemsAdded.connect(self.handleItemsAdded)
+            self._dataSet.dataCleared.connect(self.handleDataCleared)
+        self.resolveDataSet()
+
+    def dataSet(self):
+        return self._dataSet.data()
+
+    # Map key (row, column, value) to value index in data item (VariantItem).
+    # Doesn't gain ownership of mapping, but does connect to it to listen for
+    # mapping changes. Modifying mapping that is set to proxy will trigger
+    # dataset re-resolving.
+    def setMapping(self, mapping):
+        if self._mapping:
+            self._mapping.mappingChanged.disconnect(self.handleMappingChanged)
+
+        self._mapping = mapping
+
+        if self._mapping:
+            self._mapping.mappingChanged.connect(self.handleMappingChanged)
+
+        self.resolveDataSet()
+
+    def mapping(self):
+        return self._mapping.data()
+
+    @Slot(int, int)
+    def handleItemsAdded(self, index, count):
+        # Resolve new items
+        self.resolveDataSet()
+
+    @Slot()
+    def handleDataCleared(self):
+        # Data cleared, reset array
+        self.resetArray(None)
+
+    @Slot()
+    def handleMappingChanged(self):
+        self.resolveDataSet()
+
+    # Resolve entire dataset into QBarDataArray.
+    def resolveDataSet(self):
+        # If we have no data or mapping, or the categories are not defined,
+        # simply clear the array
+        if (not self._dataSet or not self._mapping
+                or not self._mapping.rowCategories()
+                or not self._mapping.columnCategories()):
+            self.resetArray()
+            return
+
+        itemList = self._dataSet.itemList()
+
+        rowIndex = self._mapping.rowIndex()
+        columnIndex = self._mapping.columnIndex()
+        valueIndex = self._mapping.valueIndex()
+        rowList = self._mapping.rowCategories()
+        columnList = self._mapping.columnCategories()
+
+        # Sort values into rows and columns
+        itemValueMap = {}
+        for item in itemList:
+            key = str(item[rowIndex])
+            v = itemValueMap.get(key)
+            if not v:
+                v = {}
+                itemValueMap[key] = v
+            v[str(item[columnIndex])] = float(item[valueIndex])
+
+        # Create a new data array in format the parent class understands
+        newProxyArray = []
+        for rowKey in rowList:
+            newProxyRow = []
+            for i in range(0, len(columnList)):
+                item = QBarDataItem(itemValueMap[rowKey][columnList[i]])
+                newProxyRow.append(item)
+            newProxyArray.append(newProxyRow)
+
+        # Finally, reset the data array in the parent class
+        self.resetArray(newProxyArray)
diff --git a/examples/graphs/3d/widgetgraphgallery/variantdataset.py b/examples/graphs/3d/widgetgraphgallery/variantdataset.py
new file mode 100644 (file)
index 0000000..c9b8ab1
--- /dev/null
@@ -0,0 +1,40 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QObject, Signal
+
+
+class VariantDataSet(QObject):
+
+    itemsAdded = Signal(int, int)
+    dataCleared = Signal()
+
+    def __init__(self):
+        super().__init__()
+        self._variantData = []
+
+    def clear(self):
+        for item in self._variantData:
+            item.clear()
+            del item
+
+        self._variantData.clear()
+        self.dataCleared.emit()
+
+    def addItem(self, item):
+        self._variantData.append(item)
+        addIndex = len(self._variantData)
+
+        self.itemsAdded.emit(addIndex, 1)
+        return addIndex
+
+    def addItems(self, itemList):
+        newCount = len(itemList)
+        addIndex = len(self._variantData)
+        self._variantData.extend(itemList)
+        self.itemsAdded.emit(addIndex, newCount)
+        return addIndex
+
+    def itemList(self):
+        return self._variantData
diff --git a/examples/graphs/3d/widgetgraphgallery/widgetgraphgallery.pyproject b/examples/graphs/3d/widgetgraphgallery/widgetgraphgallery.pyproject
new file mode 100644 (file)
index 0000000..ebc680f
--- /dev/null
@@ -0,0 +1,27 @@
+{
+    "files": ["main.py",
+              "bargraph.py",
+              "graphmodifier.py",
+              "highlightseries.py",
+              "rainfalldata.py",
+              "scatterdatamodifier.py",
+              "scattergraph.py",
+              "surfacegraph.py",
+              "surfacegraphmodifier.py",
+              "topographicseries.py",
+              "variantbardatamapping.py",
+              "variantbardataproxy.py",
+              "variantdataset.py",
+              "data/layer_1.png",
+              "data/layer_2.png",
+              "data/layer_3.png",
+              "data/license.txt",
+              "data/maptexture.jpg",
+              "data/narrowarrow.mesh",
+              "data/oilrig.mesh",
+              "data/pipe.mesh",
+              "data/raindata.txt",
+              "data/refinery.mesh",
+              "data/topography.png"
+]
+}
index c0e00dd93204d7a11c404ff2957f81f611e8721d..28ce3454cf378bcc498136f7e2590d84f6ff258a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -28,10 +29,10 @@ class AnalogClockWindow(QRasterWindow):
                                        QPoint(-1, -89), QPoint(1, -89)])
 
         palette = qApp.palette()  # noqa: F821
-        self._background_color = palette.color(QPalette.Window)
-        self._hour_color = palette.color(QPalette.Text)
-        self._minute_color = palette.color(QPalette.Text)
-        self._seconds_color = palette.color(QPalette.Accent)
+        self._background_color = palette.color(QPalette.ColorRole.Window)
+        self._hour_color = palette.color(QPalette.ColorRole.Text)
+        self._minute_color = palette.color(QPalette.ColorRole.Text)
+        self._seconds_color = palette.color(QPalette.ColorRole.Accent)
 
     def paintEvent(self, e):
         with QPainter(self) as painter:
@@ -44,13 +45,13 @@ class AnalogClockWindow(QRasterWindow):
         side = min(width, height)
 
         painter.fillRect(0, 0, width, height, self._background_color)
-        painter.setRenderHint(QPainter.Antialiasing)
+        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
         painter.translate(width / 2, height / 2)
         painter.scale(side / 200.0, side / 200.0)
 
         time = QTime.currentTime()
 
-        painter.setPen(Qt.NoPen)
+        painter.setPen(Qt.PenStyle.NoPen)
         painter.setBrush(self._hour_color)
 
         painter.save()
index defbc46ef0d2d2e00e1d6c0ff097655456c83acb..9d312080a8660f51572721b90a901dd57b4f042f 100644 (file)
@@ -1,5 +1,3 @@
-.. _rhi-window-example:
-
 RHI Window Example
 ==================
 
index cfb73b10bdfb67bcdc14afa36e7e00b7a1aca446..6c0f313b0d7a230e635995960e6eef5904e80b01 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from argparse import ArgumentParser, RawDescriptionHelpFormatter
 import sys
@@ -14,11 +15,11 @@ if __name__ == "__main__":
     app = QGuiApplication(sys.argv)
 
     # Use platform-specific defaults when no command-line arguments given.
-    graphicsApi = QRhi.OpenGLES2
+    graphicsApi = QRhi.Implementation.OpenGLES2
     if sys.platform == "win32":
-        graphicsApi = QRhi.D3D11
+        graphicsApi = QRhi.Implementation.D3D11
     elif sys.platform == "darwin":
-        graphicsApi = QRhi.Metal
+        graphicsApi = QRhi.Implementation.Metal
 
     parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter,
                             description="QRhi render example")
@@ -32,15 +33,15 @@ if __name__ == "__main__":
                         help="Metal")
     args = parser.parse_args()
     if args.null:
-        graphicsApi = QRhi.Null
+        graphicsApi = QRhi.Implementation.Null
     elif args.opengl:
-        graphicsApi = QRhi.OpenGLES2
+        graphicsApi = QRhi.Implementation.OpenGLES2
     elif args.d3d11:
-        graphicsApi = QRhi.D3D11
+        graphicsApi = QRhi.Implementation.D3D11
     elif args.d3d12:
-        graphicsApi = QRhi.D3D12
+        graphicsApi = QRhi.Implementation.D3D12
     elif args.metal:
-        graphicsApi = QRhi.Metal
+        graphicsApi = QRhi.Implementation.Metal
 
     # graphicsApi = QRhi.Vulkan?  detect? needs QVulkanInstance
 
@@ -56,7 +57,7 @@ if __name__ == "__main__":
     # we get on macOS is not sufficient.
     if sys.platform == "darwin":
         fmt.setVersion(4, 1)
-        fmt.setProfile(QSurfaceFormat.CoreProfile)
+        fmt.setProfile(QSurfaceFormat.OpenGLContextProfile.CoreProfile)
     QSurfaceFormat.setDefaultFormat(fmt)
 
     window = HelloWindow(graphicsApi)
index fe054af4883d7223b6723c92b776e029a3c0b68d..d4522e5126a141e4e386c813962fd1ece581d34b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import numpy
 import sys
@@ -37,7 +38,7 @@ UBUF_SIZE = 68
 
 def getShader(name):
     f = QFile(name)
-    if f.open(QIODevice.ReadOnly):
+    if f.open(QIODevice.OpenModeFlag.ReadOnly):
         result = QShader.fromSerialized(f.readAll())
         f.close()
         return result
@@ -48,7 +49,7 @@ class RhiWindow(QWindow):
 
     def __init__(self, graphicsApi):
         super().__init__()
-        self.m_graphicsApi = QRhi.Null
+        self.m_graphicsApi = QRhi.Implementation.Null
         self.m_initialized = False
         self.m_notExposed = False
         self.m_newlyExposed = False
@@ -63,15 +64,15 @@ class RhiWindow(QWindow):
 
         self.m_graphicsApi = graphicsApi
 
-        if graphicsApi == QRhi.OpenGLES2:
+        if graphicsApi == QRhi.Implementation.OpenGLES2:
             self.setSurfaceType(QSurface.SurfaceType.OpenGLSurface)
-        elif graphicsApi == QRhi.Vulkan:
+        elif graphicsApi == QRhi.Implementation.Vulkan:
             self.setSurfaceType(QSurface.SurfaceType.VulkanSurface)
-        elif graphicsApi == QRhi.D3D11 or graphicsApi == QRhi.D3D12:
+        elif graphicsApi == QRhi.Implementation.D3D11 or graphicsApi == QRhi.Implementation.D3D12:
             self.setSurfaceType(QSurface.SurfaceType.Direct3DSurface)
-        elif graphicsApi == QRhi.Metal:
+        elif graphicsApi == QRhi.Implementation.Metal:
             self.setSurfaceType(QSurface.SurfaceType.MetalSurface)
-        elif graphicsApi == QRhi.Null:
+        elif graphicsApi == QRhi.Implementation.Null:
             pass  # RasterSurface
 
     def __del__(self):
@@ -91,17 +92,17 @@ class RhiWindow(QWindow):
             self.m_fallbackSurface = None
 
     def graphicsApiName(self):
-        if self.m_graphicsApi == QRhi.Null:
+        if self.m_graphicsApi == QRhi.Implementation.Null:
             return "Null (no output)"
-        if self.m_graphicsApi == QRhi.OpenGLES2:
+        if self.m_graphicsApi == QRhi.Implementation.OpenGLES2:
             return "OpenGL"
-        if self.m_graphicsApi == QRhi.Vulkan:
+        if self.m_graphicsApi == QRhi.Implementation.Vulkan:
             return "Vulkan"
-        if self.m_graphicsApi == QRhi.D3D11:
+        if self.m_graphicsApi == QRhi.Implementation.D3D11:
             return "Direct3D 11"
-        if self.m_graphicsApi == QRhi.D3D12:
+        if self.m_graphicsApi == QRhi.Implementation.D3D12:
             return "Direct3D 12"
-        if self.m_graphicsApi == QRhi.Metal:
+        if self.m_graphicsApi == QRhi.Implementation.Metal:
             return "Metal"
         return ""
 
@@ -140,51 +141,51 @@ class RhiWindow(QWindow):
             self.render()
 
     def event(self, e):
-        if e.type() == QEvent.UpdateRequest:
+        if e.type() == QEvent.Type.UpdateRequest:
             self.render()
-        elif e.type() == QEvent.PlatformSurface:
+        elif e.type() == QEvent.Type.PlatformSurface:
             # this is the proper time to tear down the swapchain (while
             # the native window and surface are still around)
-            if e.surfaceEventType() == QPlatformSurfaceEvent.SurfaceAboutToBeDestroyed:
+            if e.surfaceEventType() == QPlatformSurfaceEvent.SurfaceEventType.SurfaceAboutToBeDestroyed:  # noqa: E501
                 self.releaseSwapChain()
 
         return super().event(e)
 
     def init(self):
-        if self.m_graphicsApi == QRhi.Null:
+        if self.m_graphicsApi == QRhi.Implementation.Null:
             params = QRhiNullInitParams()
-            self.m_rhi = QRhi.create(QRhi.Null, params)
+            self.m_rhi = QRhi.create(QRhi.Implementation.Null, params)
 
-        if self.m_graphicsApi == QRhi.OpenGLES2:
+        if self.m_graphicsApi == QRhi.Implementation.OpenGLES2:
             self.m_fallbackSurface = QRhiGles2InitParams.newFallbackSurface()
             params = QRhiGles2InitParams()
             params.fallbackSurface = self.m_fallbackSurface
             params.window = self
-            self.m_rhi = QRhi.create(QRhi.OpenGLES2, params)
-        elif self.m_graphicsApi == QRhi.D3D11:
+            self.m_rhi = QRhi.create(QRhi.Implementation.OpenGLES2, params)
+        elif self.m_graphicsApi == QRhi.Implementation.D3D11:
             params = QRhiD3D11InitParams()
             # Enable the debug layer, if available. This is optional
             # and should be avoided in production builds.
             params.enableDebugLayer = True
-            self.m_rhi = QRhi.create(QRhi.D3D11, params)
-        elif self.m_graphicsApi == QRhi.D3D12:
+            self.m_rhi = QRhi.create(QRhi.Implementation.D3D11, params)
+        elif self.m_graphicsApi == QRhi.Implementation.D3D12:
             params = QRhiD3D12InitParams()
             # Enable the debug layer, if available. This is optional
             # and should be avoided in production builds.
             params.enableDebugLayer = True
-            self.m_rhi = QRhi.create(QRhi.D3D12, params)
-        elif self.m_graphicsApi == QRhi.Metal:
+            self.m_rhi = QRhi.create(QRhi.Implementation.D3D12, params)
+        elif self.m_graphicsApi == QRhi.Implementation.Metal:
             params = QRhiMetalInitParams()
-            self.m_rhi.reset(QRhi.create(QRhi.Metal, params))
+            self.m_rhi.reset(QRhi.create(QRhi.Implementation.Metal, params))
 
         if not self.m_rhi:
             qFatal("Failed to create RHI backend")
 
         self.m_sc = self.m_rhi.newSwapChain()
         # no need to set the size here, due to UsedWithSwapChainOnly
-        self.m_ds = self.m_rhi.newRenderBuffer(QRhiRenderBuffer.DepthStencil,
+        self.m_ds = self.m_rhi.newRenderBuffer(QRhiRenderBuffer.Type.DepthStencil,
                                                QSize(), 1,
-                                               QRhiRenderBuffer.UsedWithSwapChainOnly)
+                                               QRhiRenderBuffer.Flag.UsedWithSwapChainOnly)
         self.m_sc.setWindow(self)
         self.m_sc.setDepthStencil(self.m_ds)
         self.m_rp = self.m_sc.newCompatibleRenderPassDescriptor()
@@ -223,13 +224,13 @@ class RhiWindow(QWindow):
             self.m_newlyExposed = False
 
         result = self.m_rhi.beginFrame(self.m_sc)
-        if result == QRhi.FrameOpSwapChainOutOfDate:
+        if result == QRhi.FrameOpResult.FrameOpSwapChainOutOfDate:
             self.resizeSwapChain()
             if not self.m_hasSwapChain:
                 return
             result = self.m_rhi.beginFrame(self.m_sc)
 
-        if result != QRhi.FrameOpSuccess:
+        if result != QRhi.FrameOpResult.FrameOpSuccess:
             qWarning(f"beginFrame failed with {result}, will retry")
             self.requestUpdate()
             return
@@ -268,19 +269,19 @@ class HelloWindow(RhiWindow):
             return
 
         if not self.m_texture:
-            self.m_texture = self.m_rhi.newTexture(QRhiTexture.RGBA8, pixelSize)
+            self.m_texture = self.m_rhi.newTexture(QRhiTexture.Format.RGBA8, pixelSize)
         else:
             self.m_texture.setPixelSize(pixelSize)
         self.m_texture.create()
-        image = QImage(pixelSize, QImage.Format_RGBA8888_Premultiplied)
+        image = QImage(pixelSize, QImage.Format.Format_RGBA8888_Premultiplied)
         with QPainter(image) as painter:
             painter.fillRect(QRectF(QPointF(0, 0), pixelSize),
                              QColor.fromRgbF(0.4, 0.7, 0.0, 1.0))
-            painter.setPen(Qt.transparent)
-            painter.setBrush(QGradient(QGradient.DeepBlue))
+            painter.setPen(Qt.GlobalColor.transparent)
+            painter.setBrush(QGradient(QGradient.Preset.DeepBlue))
             painter.drawRoundedRect(QRectF(QPointF(20, 20), pixelSize - QSize(40, 40)),
                                     16, 16)
-            painter.setPen(Qt.black)
+            painter.setPen(Qt.GlobalColor.black)
             font = QFont()
             font.setPixelSize(0.05 * min(pixelSize.width(), pixelSize.height()))
             painter.setFont(font)
@@ -298,26 +299,29 @@ class HelloWindow(RhiWindow):
         self.m_initialUpdates = self.m_rhi.nextResourceUpdateBatch()
 
         vertex_size = 4 * VERTEX_DATA.size
-        self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Immutable, QRhiBuffer.VertexBuffer,
+        self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Immutable,
+                                           QRhiBuffer.UsageFlag.VertexBuffer,
                                            vertex_size)
         self.m_vbuf.create()
         self.m_initialUpdates.uploadStaticBuffer(self.m_vbuf,
                                                  VoidPtr(VERTEX_DATA.tobytes(), vertex_size))
 
-        self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Dynamic,
-                                           QRhiBuffer.UniformBuffer, UBUF_SIZE)
+        self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Dynamic,
+                                           QRhiBuffer.UsageFlag.UniformBuffer, UBUF_SIZE)
         self.m_ubuf.create()
 
         self.ensureFullscreenTexture(self.m_sc.surfacePixelSize(), self.m_initialUpdates)
 
-        self.m_sampler = self.m_rhi.newSampler(QRhiSampler.Linear, QRhiSampler.Linear,
-                                               QRhiSampler.None_,
-                                               QRhiSampler.ClampToEdge, QRhiSampler.ClampToEdge)
+        self.m_sampler = self.m_rhi.newSampler(QRhiSampler.Filter.Linear,
+                                               QRhiSampler.Filter.Linear,
+                                               QRhiSampler.Filter.None_,
+                                               QRhiSampler.AddressMode.ClampToEdge,
+                                               QRhiSampler.AddressMode.ClampToEdge)
         self.m_sampler.create()
 
         self.m_colorTriSrb = self.m_rhi.newShaderResourceBindings()
-        visibility = (QRhiShaderResourceBinding.VertexStage
-                      | QRhiShaderResourceBinding.FragmentStage)
+        visibility = (QRhiShaderResourceBinding.StageFlag.VertexStage
+                      | QRhiShaderResourceBinding.StageFlag.FragmentStage)
         bindings = [
             QRhiShaderResourceBinding.uniformBuffer(0, visibility, self.m_ubuf)
         ]
@@ -334,16 +338,17 @@ class HelloWindow(RhiWindow):
         premulAlphaBlend.enable = True
         self.m_colorPipeline.setTargetBlends([premulAlphaBlend])
         stages = [
-            QRhiShaderStage(QRhiShaderStage.Vertex, getShader(":/color.vert.qsb")),
-            QRhiShaderStage(QRhiShaderStage.Fragment, getShader(":/color.frag.qsb"))
+            QRhiShaderStage(QRhiShaderStage.Type.Vertex, getShader(":/color.vert.qsb")),
+            QRhiShaderStage(QRhiShaderStage.Type.Fragment, getShader(":/color.frag.qsb"))
         ]
         self.m_colorPipeline.setShaderStages(stages)
         inputLayout = QRhiVertexInputLayout()
         input_bindings = [QRhiVertexInputBinding(5 * 4)]  # sizeof(float)
         inputLayout.setBindings(input_bindings)
         attributes = [
-            QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Float2, 0),
-            QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Float3, 2 * 4)]  # sizeof(float)
+            QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Format.Float2, 0),
+            # sizeof(float)
+            QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Format.Float3, 2 * 4)]
         inputLayout.setAttributes(attributes)
         self.m_colorPipeline.setVertexInputLayout(inputLayout)
         self.m_colorPipeline.setShaderResourceBindings(self.m_colorTriSrb)
@@ -352,7 +357,7 @@ class HelloWindow(RhiWindow):
 
         self.m_fullscreenQuadSrb = self.m_rhi.newShaderResourceBindings()
         bindings = [
-            QRhiShaderResourceBinding.sampledTexture(0, QRhiShaderResourceBinding.FragmentStage,
+            QRhiShaderResourceBinding.sampledTexture(0, QRhiShaderResourceBinding.StageFlag.FragmentStage,  # noqa: E501
                                                      self.m_texture, self.m_sampler)
         ]
         self.m_fullscreenQuadSrb.setBindings(bindings)
@@ -360,8 +365,8 @@ class HelloWindow(RhiWindow):
 
         self.m_fullscreenQuadPipeline = self.m_rhi.newGraphicsPipeline()
         stages = [
-            QRhiShaderStage(QRhiShaderStage.Vertex, getShader(":/quad.vert.qsb")),
-            QRhiShaderStage(QRhiShaderStage.Fragment, getShader(":/quad.frag.qsb"))
+            QRhiShaderStage(QRhiShaderStage.Type.Vertex, getShader(":/quad.vert.qsb")),
+            QRhiShaderStage(QRhiShaderStage.Type.Fragment, getShader(":/quad.frag.qsb"))
         ]
         self.m_fullscreenQuadPipeline.setShaderStages(stages)
         layout = QRhiVertexInputLayout()
@@ -402,7 +407,7 @@ class HelloWindow(RhiWindow):
         self.ensureFullscreenTexture(outputSizeInPixels, resourceUpdates)
 
         cv = QRhiDepthStencilClearValue(1.0, 0)
-        cb.beginPass(self.m_sc.currentFrameRenderTarget(), Qt.black,
+        cb.beginPass(self.m_sc.currentFrameRenderTarget(), Qt.GlobalColor.black,
                      cv, resourceUpdates)
 
         cb.setGraphicsPipeline(self.m_fullscreenQuadPipeline)
diff --git a/examples/httpserver/afterrequest/doc/afterrequest.rst b/examples/httpserver/afterrequest/doc/afterrequest.rst
deleted file mode 100644 (file)
index 0e81a67..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-HTTP Server After Request Example
-=================================
-
-A Python application that demonstrates the analogous example in C++
-`AfterRequest Example <https://doc.qt.io/qt-6/qthttpserver-afterrequest-example.html>`_
diff --git a/examples/httpserver/afterrequest/main.py b/examples/httpserver/afterrequest/main.py
deleted file mode 100644 (file)
index e68c992..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-"""PySide6 port of the /httpserver/afterrequest from from Qt"""
-
-import sys
-
-from PySide6.QtCore import QCoreApplication
-from PySide6.QtNetwork import QHostAddress
-from PySide6.QtHttpServer import QHttpServer
-
-
-def route(request):
-    return "Hello world"
-
-
-def after_request(response, request):
-    response.setHeader(b"Server", b"Super server!")
-
-
-if __name__ == '__main__':
-    app = QCoreApplication(sys.argv)
-    httpServer = QHttpServer()
-    httpServer.route("/", route)
-
-    httpServer.afterRequest(after_request)
-
-    port = httpServer.listen(QHostAddress.Any)
-    if port == 0:
-        print("Server failed to listen on a port.", file=sys.stderr)
-        sys.exit(-1)
-
-    print(f"Running on http://127.0.0.1:{port}/ (Press CTRL+\\ to quit)")
-
-    sys.exit(app.exec())
diff --git a/examples/httpserver/simplehttpserver/doc/simplehttpserver.rst b/examples/httpserver/simplehttpserver/doc/simplehttpserver.rst
new file mode 100644 (file)
index 0000000..f23998e
--- /dev/null
@@ -0,0 +1,5 @@
+Simple HTTP Server Example
+==========================
+
+A simplified version of the C++ example
+`Simple HTTP Server Example <https://doc.qt.io/qt-6/qthttpserver-simple-example.html>`_
diff --git a/examples/httpserver/simplehttpserver/main.py b/examples/httpserver/simplehttpserver/main.py
new file mode 100644 (file)
index 0000000..eb3f569
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+"""PySide6 port of the /httpserver/afterrequest from from Qt"""
+
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtNetwork import QHttpHeaders, QTcpServer
+from PySide6.QtHttpServer import QHttpServer
+
+
+def route(request):
+    return "Hello world"
+
+
+def after_request(request, response):
+    headers = response.headers()
+    headers.append(QHttpHeaders.WellKnownHeader.WWWAuthenticate,
+                   'Basic realm="Simple example", charset="UTF-8"')
+    response.setHeaders(headers)
+
+
+if __name__ == '__main__':
+    app = QCoreApplication(sys.argv)
+    httpServer = QHttpServer()
+    httpServer.route("/", route)
+
+    httpServer.addAfterRequestHandler(httpServer, after_request)
+
+    tcpServer = QTcpServer()
+    if not tcpServer.listen() or not httpServer.bind(tcpServer):
+        print("Server failed to listen on a port.", file=sys.stderr)
+        sys.exit(-1)
+    port = tcpServer.serverPort()
+
+    print(f"Running on http://127.0.0.1:{port}/ (Press CTRL+\\ to quit)")
+
+    sys.exit(app.exec())
diff --git a/examples/httpserver/simplehttpserver/simplehttpserver.pyproject b/examples/httpserver/simplehttpserver/simplehttpserver.pyproject
new file mode 100644 (file)
index 0000000..cc7a74a
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "files": ["main.py"]
+}
index 3921b5d511effb16b3cadee40064d1650164e4d2..162327c4ff4300d9dcf97f5994e6dec9b2bbeca2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 hello.py
@@ -35,7 +36,7 @@ class MyWidget(QWidget):
 
         self.button = QPushButton("Click me!")
         self.text = QLabel(f"Hello World auto_quit={auto_quit}")
-        self.text.setAlignment(Qt.AlignCenter)
+        self.text.setAlignment(Qt.AlignmentFlag.AlignCenter)
 
         self.layout = QVBoxLayout()
         self.layout.addWidget(self.text)
index 426c72757d474456f877fd5645543e1c09c3a1f2..f2206111974cdc51d03480a24359479e9f9470b2 100644 (file)
@@ -66,4 +66,3 @@ Item {
         }
     }
 }
-
index 24ae1623f8e144b1ff64745da1b3b51703b527e2..1eefb497cf87fafbc97a5cd043582f2982bf1470 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the location/mapviewer example from Qt v6.x"""
 
@@ -70,6 +71,6 @@ if __name__ == "__main__":
     QMetaObject.invokeMethod(items[0], "initializeProviders",
                              Q_ARG("QVariant", parameters))
 
-    ex = application.exec()
+    exit_code = application.exec()
     del engine
-    sys.exit(ex)
+    sys.exit(exit_code)
index 06d52f68a8e45385bbed97317efabf09c8766f99..b0ab567d863a60bfb7641480d1811987b098eccd 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the multimedia/audiooutput example from Qt v5.x, originating from PyQt"""
 
@@ -28,7 +29,7 @@ class Generator(QIODevice):
         self.generate_data(format, durationUs, sampleRate)
 
     def start(self):
-        self.open(QIODevice.ReadOnly)
+        self.open(QIODevice.OpenModeFlag.ReadOnly)
 
     def stop(self):
         self.m_pos = 0
@@ -39,18 +40,18 @@ class Generator(QIODevice):
 
         sample_size = fmt.bytesPerSample() * 8
         if sample_size == 8:
-            if fmt.sampleFormat() == QAudioFormat.UInt8:
+            if fmt.sampleFormat() == QAudioFormat.SampleFormat.UInt8:
                 scaler = lambda x: ((1.0 + x) / 2 * 255)  # noqa: E731
                 pack_format = 'B'
-            elif fmt.sampleFormat() == QAudioFormat.Int16:
+            elif fmt.sampleFormat() == QAudioFormat.SampleFormat.Int16:
                 scaler = lambda x: x * 127  # noqa: E731
                 pack_format = 'b'
         elif sample_size == 16:
-            little_endian = QSysInfo.ByteOrder == QSysInfo.LittleEndian
-            if fmt.sampleFormat() == QAudioFormat.UInt8:
+            little_endian = QSysInfo.Endian.ByteOrder == QSysInfo.Endian.LittleEndian
+            if fmt.sampleFormat() == QAudioFormat.SampleFormat.UInt8:
                 scaler = lambda x: (1.0 + x) / 2 * 65535  # noqa: E731
                 pack_format = '<H' if little_endian else '>H'
-            elif fmt.sampleFormat() == QAudioFormat.Int16:
+            elif fmt.sampleFormat() == QAudioFormat.SampleFormat.Int16:
                 scaler = lambda x: x * 32767  # noqa: E731
                 pack_format = '<h' if little_endian else '>h'
 
@@ -139,7 +140,8 @@ class AudioTest(QMainWindow):
 
         volume_box = QHBoxLayout()
         volume_label = QLabel("Volume:")
-        self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100, singleStep=10)
+        self.m_volumeSlider = QSlider(Qt.Orientation.Horizontal,
+                                      minimum=0, maximum=100, singleStep=10)
         self.m_volumeSlider.valueChanged.connect(self.volume_changed)
 
         volume_box.addWidget(volume_label)
@@ -157,7 +159,7 @@ class AudioTest(QMainWindow):
         self.m_format = QAudioFormat()
         self.m_format.setSampleRate(self.DATA_SAMPLE_RATE_HZ)
         self.m_format.setChannelCount(1)
-        self.m_format.setSampleFormat(QAudioFormat.Int16)
+        self.m_format.setSampleFormat(QAudioFormat.SampleFormat.Int16)
 
         info = self.m_devices[0]
         if not info.isFormatSupported(self.m_format):
@@ -209,7 +211,7 @@ class AudioTest(QMainWindow):
 
     @Slot()
     def pull_timer_expired(self):
-        if self.m_audioSink is not None and self.m_audioSink.state() != QAudio.StoppedState:
+        if self.m_audioSink is not None and self.m_audioSink.state() != QAudio.State.StoppedState:
             bytes_free = self.m_audioSink.bytesFree()
             data = self.m_generator.read(bytes_free)
             if data:
@@ -234,26 +236,26 @@ class AudioTest(QMainWindow):
 
     @Slot()
     def toggle_suspend_resume(self):
-        if self.m_audioSink.state() == QAudio.SuspendedState:
+        if self.m_audioSink.state() == QAudio.State.SuspendedState:
             qWarning("status: Suspended, resume()")
             self.m_audioSink.resume()
             self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
-        elif self.m_audioSink.state() == QAudio.ActiveState:
+        elif self.m_audioSink.state() == QAudio.State.ActiveState:
             qWarning("status: Active, suspend()")
             self.m_audioSink.suspend()
             self.m_suspendResumeButton.setText(self.RESUME_LABEL)
-        elif self.m_audioSink.state() == QAudio.StoppedState:
+        elif self.m_audioSink.state() == QAudio.State.StoppedState:
             qWarning("status: Stopped, resume()")
             self.m_audioSink.resume()
             self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
-        elif self.m_audioSink.state() == QAudio.IdleState:
+        elif self.m_audioSink.state() == QAudio.State.IdleState:
             qWarning("status: IdleState")
 
     state_map = {
-        QAudio.ActiveState: "ActiveState",
-        QAudio.SuspendedState: "SuspendedState",
-        QAudio.StoppedState: "StoppedState",
-        QAudio.IdleState: "IdleState"}
+        QAudio.State.ActiveState: "ActiveState",
+        QAudio.State.SuspendedState: "SuspendedState",
+        QAudio.State.StoppedState: "StoppedState",
+        QAudio.State.IdleState: "IdleState"}
 
     @Slot("QAudio::State")
     def handle_state_changed(self, state):
index a78beb584fca07343527961270ac90736acfa7c6..1c0d9841255b3a813b320c7f1d953e4efb237045 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 PySide6 port of Qt6 example examples/multimedia/audiosources
@@ -14,7 +15,6 @@ QIODevice have python bindings that needs to be fixed.
 """
 import os
 import sys
-from typing import Optional
 
 import PySide6
 from PySide6.QtCore import QByteArray, QMargins, Qt, Slot, qWarning
@@ -56,10 +56,10 @@ class AudioInfo:
 
 
 class RenderArea(QWidget):
-    def __init__(self, parent: Optional[PySide6.QtWidgets.QWidget] = None) -> None:
+    def __init__(self, parent: PySide6.QtWidgets.QWidget | None = None) -> None:
         super().__init__(parent=parent)
         self.m_level = 0
-        self.setBackgroundRole(QPalette.Base)
+        self.setBackgroundRole(QPalette.ColorRole.Base)
         self.setAutoFillBackground(True)
         self.setMinimumHeight(30)
         self.setMinimumWidth(200)
@@ -70,7 +70,7 @@ class RenderArea(QWidget):
 
     def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
         with QPainter(self) as painter:
-            painter.setPen(Qt.black)
+            painter.setPen(Qt.GlobalColor.black)
             frame = painter.viewport() - QMargins(10, 10, 10, 10)
 
             painter.drawRect(frame)
@@ -79,9 +79,8 @@ class RenderArea(QWidget):
                 return
 
             pos: int = round((frame.width() - 1) * self.m_level)
-            painter.fillRect(
-                frame.left() + 1, frame.top() + 1, pos, frame.height() - 1, Qt.red
-            )
+            painter.fillRect(frame.left() + 1, frame.top() + 1, pos, frame.height() - 1,
+                             Qt.GlobalColor.red)
 
 
 class InputTest(QWidget):
@@ -134,7 +133,7 @@ class InputTest(QWidget):
         self.m_device_box.activated[int].connect(self.device_changed)
         self.layout.addWidget(self.m_device_box)
 
-        self.m_volume_slider = QSlider(Qt.Horizontal, self)
+        self.m_volume_slider = QSlider(Qt.Orientation.Horizontal, self)
         self.m_volume_slider.setRange(0, 100)
         self.m_volume_slider.setValue(100)
         self.m_volume_slider.valueChanged.connect(self.slider_changed)
@@ -152,22 +151,22 @@ class InputTest(QWidget):
         self.layout = QVBoxLayout(self)
         error_label = QLabel(self.tr("Microphone permission is not granted!"))
         error_label.setWordWrap(True)
-        error_label.setAlignment(Qt.AlignCenter)
+        error_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
         self.layout.addWidget(error_label)
 
     def initialize_audio(self, device_info: QAudioDevice):
         format = QAudioFormat()
         format.setSampleRate(8000)
         format.setChannelCount(1)
-        format.setSampleFormat(QAudioFormat.Int16)
+        format.setSampleFormat(QAudioFormat.SampleFormat.Int16)
 
         self.m_audio_info = AudioInfo(format)
 
         self.m_audio_input = QAudioSource(device_info, format)
         initial_volume = QAudio.convertVolume(
             self.m_audio_input.volume(),
-            QAudio.LinearVolumeScale,
-            QAudio.LogarithmicVolumeScale,
+            QAudio.VolumeScale.LinearVolumeScale,
+            QAudio.VolumeScale.LogarithmicVolumeScale,
         )
         self.m_volume_slider.setValue(int(round(initial_volume * 100)))
         self.toggle_mode()
@@ -196,10 +195,10 @@ class InputTest(QWidget):
     def toggle_suspend(self):
         # toggle suspend/resume
         state = self.m_audio_input.state()
-        if (state == QAudio.SuspendedState) or (state == QAudio.StoppedState):
+        if (state == QAudio.State.SuspendedState) or (state == QAudio.State.StoppedState):
             self.m_audio_input.resume()
             self.m_suspend_resume_button.setText("Suspend recording")
-        elif state == QAudio.ActiveState:
+        elif state == QAudio.State.ActiveState:
             self.m_audio_input.suspend()
             self.m_suspend_resume_button.setText("Resume recording")
         # else no-op
@@ -212,10 +211,9 @@ class InputTest(QWidget):
 
     @Slot(int)
     def slider_changed(self, value):
-        linearVolume = QAudio.convertVolume(
-            value / float(100), QAudio.LogarithmicVolumeScale, QAudio.LinearVolumeScale
-        )
-
+        linearVolume = QAudio.convertVolume(value / float(100),
+                                            QAudio.VolumeScale.LogarithmicVolumeScale,
+                                            QAudio.VolumeScale.LinearVolumeScale)
         self.m_audio_input.setVolume(linearVolume)
 
 
index 3a247c503006c3879957ae23b26e9b4b84ce9f95..9b0d98bf6c05486c53bd0b4ab164a5223a4bf420 100644 (file)
@@ -4,10 +4,9 @@ Audio Source Example
 .. tags:: Android
 
 A Python application that demonstrates the analogous example in C++
-`Audio Source Example <https://doc-snapshots.qt.io/qt6-dev/qtmultimedia-multimedia-audiosource-example.html>`_
+`Audio Source Example <https://doc.qt.io/qt-6/qtmultimedia-audiosource-example.html>`_
 
 
 .. image:: audiosource.png
    :width: 400
    :alt: audiosource example
-
index fa379c8076aa5492f9175b65c97ee5f2285fbc91..ecc8f70109bb972bdb075c2994a64f6990efd8f3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 import sys
@@ -152,10 +153,10 @@ class Camera(QMainWindow):
             return
 
         key = event.key()
-        if key == Qt.Key_CameraFocus:
+        if key == Qt.Key.Key_CameraFocus:
             self.displayViewfinder()
             event.accept()
-        elif key == Qt.Key_Camera:
+        elif key == Qt.Key.Key_Camera:
             if self.m_doImageCapture:
                 self.takeImage()
             else:
@@ -175,8 +176,9 @@ class Camera(QMainWindow):
 
     @Slot(int, QImage)
     def processCapturedImage(self, requestId, img):
-        scaled_image = img.scaled(self._ui.viewfinder.size(), Qt.KeepAspectRatio,
-                                  Qt.SmoothTransformation)
+        scaled_image = img.scaled(self._ui.viewfinder.size(),
+                                  Qt.AspectRatioMode.KeepAspectRatio,
+                                  Qt.TransformationMode.SmoothTransformation)
 
         self._ui.lastImagePreviewLabel.setPixmap(QPixmap.fromImage(scaled_image))
 
@@ -260,17 +262,17 @@ class Camera(QMainWindow):
 
     @Slot(QMediaRecorder.RecorderState)
     def updateRecorderState(self, state):
-        if state == QMediaRecorder.StoppedState:
+        if state == QMediaRecorder.RecorderState.StoppedState:
             self._ui.recordButton.setEnabled(True)
             self._ui.pauseButton.setEnabled(True)
             self._ui.stopButton.setEnabled(False)
             self._ui.metaDataButton.setEnabled(True)
-        elif state == QMediaRecorder.PausedState:
+        elif state == QMediaRecorder.RecorderState.PausedState:
             self._ui.recordButton.setEnabled(True)
             self._ui.pauseButton.setEnabled(False)
             self._ui.stopButton.setEnabled(True)
             self._ui.metaDataButton.setEnabled(False)
-        elif state == QMediaRecorder.RecordingState:
+        elif state == QMediaRecorder.RecorderState.RecordingState:
             self._ui.recordButton.setEnabled(False)
             self._ui.pauseButton.setEnabled(True)
             self._ui.stopButton.setEnabled(True)
@@ -343,8 +345,8 @@ class Camera(QMainWindow):
     def showMetaDataDialog(self):
         if not self.m_metaDataDialog:
             self.m_metaDataDialog = MetaDataDialog(self)
-        self.m_metaDataDialog.setAttribute(Qt.WA_DeleteOnClose, False)
-        if self.m_metaDataDialog.exec() == QDialog.Accepted:
+        self.m_metaDataDialog.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
+        if self.m_metaDataDialog.exec() == QDialog.DialogCode.Accepted:
             self.saveMetaData()
 
     @Slot()
@@ -354,13 +356,13 @@ class Camera(QMainWindow):
             val = self.m_metaDataDialog.m_metaDataFields[i].text()
             if val:
                 key = QMediaMetaData.Key(i)
-                if key == QMediaMetaData.CoverArtImage:
+                if key == QMediaMetaData.Key.CoverArtImage:
                     cover_art = QImage(val)
                     data.insert(key, cover_art)
-                elif key == QMediaMetaData.ThumbnailImage:
+                elif key == QMediaMetaData.Key.ThumbnailImage:
                     thumbnail = QImage(val)
                     data.insert(key, thumbnail)
-                elif key == QMediaMetaData.Date:
+                elif key == QMediaMetaData.Key.Date:
                     date = QDateTime.fromString(val)
                     data.insert(key, date)
                 else:
index 4584e909c86e5468aac22ce28d374df0c10c9dd8..a338fb513360093dd49477e0ba1724326d075fb5 100644 (file)
@@ -28,7 +28,7 @@
         <item row="3" column="0">
          <spacer name="verticalSpacer_2">
           <property name="orientation">
-           <enum>Qt::Vertical</enum>
+           <enum>Qt::Orientation::Vertical</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
            <number>2</number>
           </property>
           <property name="orientation">
-           <enum>Qt::Horizontal</enum>
+           <enum>Qt::Orientation::Horizontal</enum>
           </property>
           <property name="tickPosition">
-           <enum>QSlider::TicksAbove</enum>
+           <enum>QSlider::TickPosition::TicksAbove</enum>
           </property>
          </widget>
         </item>
         <item row="3" column="0">
          <spacer name="verticalSpacer">
           <property name="orientation">
-           <enum>Qt::Vertical</enum>
+           <enum>Qt::Orientation::Vertical</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
            </sizepolicy>
           </property>
           <property name="frameShape">
-           <enum>QFrame::Box</enum>
+           <enum>QFrame::Shape::Box</enum>
           </property>
           <property name="text">
            <string/>
      <x>0</x>
      <y>0</y>
      <width>668</width>
-     <height>19</height>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">
index 7f269b17b6b532d769eaf5179f2855c0d8e8f545..f36722ff25cbcd580b002afecc5c7d8cfa48e6b7 100644 (file)
            <number>2</number>
           </property>
           <property name="orientation">
-           <enum>Qt::Horizontal</enum>
+           <enum>Qt::Orientation::Horizontal</enum>
           </property>
           <property name="tickPosition">
-           <enum>QSlider::TicksAbove</enum>
+           <enum>QSlider::TickPosition::TicksAbove</enum>
           </property>
          </widget>
         </item>
             <item>
              <spacer name="verticalSpacer">
               <property name="orientation">
-               <enum>Qt::Vertical</enum>
+               <enum>Qt::Orientation::Vertical</enum>
               </property>
               <property name="sizeHint" stdset="0">
                <size>
            </sizepolicy>
           </property>
           <property name="frameShape">
-           <enum>QFrame::Box</enum>
+           <enum>QFrame::Shape::Box</enum>
           </property>
           <property name="text">
            <string/>
      <x>0</x>
      <y>0</y>
      <width>668</width>
-     <height>22</height>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">
index 9ca1d92ce99f5392f8aafbefbf2534298cd8894a..392681f03bf76de9ef843e8ffdd6974d2565f4ee 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtMultimedia import QImageCapture
 from PySide6.QtWidgets import QDialog
index 8c59ca01d305dbc8fbccc7a5ee130a43c39c725b..6eb37adf760042eca4779c77d275d14b214c6018 100644 (file)
@@ -53,7 +53,7 @@
          <number>4</number>
         </property>
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Orientation::Horizontal</enum>
         </property>
        </widget>
       </item>
@@ -63,7 +63,7 @@
    <item row="1" column="0">
     <spacer name="verticalSpacer">
      <property name="orientation">
-      <enum>Qt::Vertical</enum>
+      <enum>Qt::Orientation::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
    <item row="2" column="0">
     <widget class="QDialogButtonBox" name="buttonBox">
      <property name="orientation">
-      <enum>Qt::Horizontal</enum>
+      <enum>Qt::Orientation::Horizontal</enum>
      </property>
      <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
      </property>
     </widget>
    </item>
index fd4dd32e33514c3cc16c09a1eec59f9c07a9737d..004f8fac57a92a0e1b3b5ebcd2eb01a553e0aa68 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the QtMultiMedia camera example from Qt v6.x"""
 
index 97d0a36e502599932b223014d480ffc3651d9f94..16fbde107f617009677ab06c9238776dea69dcef 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtMultimedia import QMediaMetaData
 from PySide6.QtWidgets import (QDialog, QDialogButtonBox, QFileDialog,
@@ -12,11 +13,11 @@ IMAGE_FILTER = "Image Files (*.png *.jpg *.bmp)"
 
 
 def default_value(key):
-    if key == QMediaMetaData.Title:
+    if key == QMediaMetaData.Key.Title:
         return "Qt Camera Example"
-    if key == QMediaMetaData.Author:
+    if key == QMediaMetaData.Key.Author:
         return "The Qt Company"
-    if key == QMediaMetaData.Date:
+    if key == QMediaMetaData.Key.Date:
         return QDateTime.currentDateTime().toString()
     return ""
 
@@ -34,14 +35,14 @@ class MetaDataDialog(QDialog):
             line_edit = QLineEdit(default_value(key))
             line_edit.setClearButtonEnabled(True)
             self.m_metaDataFields.append(line_edit)
-            if key == QMediaMetaData.ThumbnailImage:
+            if key == QMediaMetaData.Key.ThumbnailImage:
                 open_thumbnail = QPushButton("Open")
                 open_thumbnail.clicked.connect(self.open_thumbnail_image)
                 layout = QHBoxLayout()
                 layout.addWidget(line_edit)
                 layout.addWidget(open_thumbnail)
                 meta_data_layout.addRow(label, layout)
-            elif key == QMediaMetaData.CoverArtImage:
+            elif key == QMediaMetaData.Key.CoverArtImage:
                 open_cover_art = QPushButton("Open")
                 open_cover_art.clicked.connect(self.open_cover_art_image)
                 layout = QHBoxLayout()
@@ -58,7 +59,8 @@ class MetaDataDialog(QDialog):
         dialog_layout = QVBoxLayout(self)
         dialog_layout.addWidget(scroll_area)
 
-        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+                                      | QDialogButtonBox.StandardButton.Cancel)
         dialog_layout.addWidget(button_box)
 
         self.setWindowTitle("Set Metadata")
index 690cf3352f801a669a2cc89a7705f0f12de4a457..db78ecf5a9dc193b23f1a60637cb1a7d988422c9 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'camera.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -62,8 +62,8 @@ class Ui_Camera(object):
         self.exposureCompensation.setMinimum(-4)
         self.exposureCompensation.setMaximum(4)
         self.exposureCompensation.setPageStep(2)
-        self.exposureCompensation.setOrientation(Qt.Horizontal)
-        self.exposureCompensation.setTickPosition(QSlider.TicksAbove)
+        self.exposureCompensation.setOrientation(Qt.Orientation.Horizontal)
+        self.exposureCompensation.setTickPosition(QSlider.TickPosition.TicksAbove)
 
         self.gridLayout.addWidget(self.exposureCompensation, 5, 0, 1, 1)
 
@@ -152,7 +152,7 @@ class Ui_Camera(object):
         sizePolicy1.setVerticalStretch(0)
         sizePolicy1.setHeightForWidth(self.lastImagePreviewLabel.sizePolicy().hasHeightForWidth())
         self.lastImagePreviewLabel.setSizePolicy(sizePolicy1)
-        self.lastImagePreviewLabel.setFrameShape(QFrame.Box)
+        self.lastImagePreviewLabel.setFrameShape(QFrame.Shape.Box)
 
         self.gridLayout_4.addWidget(self.lastImagePreviewLabel, 0, 0, 1, 1)
 
@@ -163,7 +163,7 @@ class Ui_Camera(object):
         Camera.setCentralWidget(self.centralwidget)
         self.menubar = QMenuBar(Camera)
         self.menubar.setObjectName(u"menubar")
-        self.menubar.setGeometry(QRect(0, 0, 668, 19))
+        self.menubar.setGeometry(QRect(0, 0, 668, 26))
         self.menuFile = QMenu(self.menubar)
         self.menuFile.setObjectName(u"menuFile")
         self.menuDevices = QMenu(self.menubar)
index 5cdd81f1e1ac318c958b60f29b2037f03ed8c858..a9bd4d0978b6bafef3af6b8b9f24abf68684e672 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'camera_mobile.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -56,8 +56,8 @@ class Ui_Camera(object):
         self.exposureCompensation.setMinimum(-4)
         self.exposureCompensation.setMaximum(4)
         self.exposureCompensation.setPageStep(2)
-        self.exposureCompensation.setOrientation(Qt.Horizontal)
-        self.exposureCompensation.setTickPosition(QSlider.TicksAbove)
+        self.exposureCompensation.setOrientation(Qt.Orientation.Horizontal)
+        self.exposureCompensation.setTickPosition(QSlider.TickPosition.TicksAbove)
 
         self.gridLayout.addWidget(self.exposureCompensation, 4, 0, 1, 1)
 
@@ -75,7 +75,7 @@ class Ui_Camera(object):
         self.takeImageButton.setObjectName(u"takeImageButton")
         self.takeImageButton.setEnabled(False)
         icon = QIcon()
-        icon.addFile(u":/images/shutter.svg", QSize(), QIcon.Normal, QIcon.Off)
+        icon.addFile(u":/images/shutter.svg", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.takeImageButton.setIcon(icon)
 
         self.gridLayout.addWidget(self.takeImageButton, 0, 0, 1, 1)
@@ -180,7 +180,7 @@ class Ui_Camera(object):
         sizePolicy4.setVerticalStretch(0)
         sizePolicy4.setHeightForWidth(self.lastImagePreviewLabel.sizePolicy().hasHeightForWidth())
         self.lastImagePreviewLabel.setSizePolicy(sizePolicy4)
-        self.lastImagePreviewLabel.setFrameShape(QFrame.Box)
+        self.lastImagePreviewLabel.setFrameShape(QFrame.Shape.Box)
 
         self.gridLayout_4.addWidget(self.lastImagePreviewLabel, 0, 0, 1, 1)
 
@@ -191,7 +191,7 @@ class Ui_Camera(object):
         Camera.setCentralWidget(self.centralwidget)
         self.menubar = QMenuBar(Camera)
         self.menubar.setObjectName(u"menubar")
-        self.menubar.setGeometry(QRect(0, 0, 668, 22))
+        self.menubar.setGeometry(QRect(0, 0, 668, 26))
         self.menuFile = QMenu(self.menubar)
         self.menuFile.setObjectName(u"menuFile")
         self.menuDevices = QMenu(self.menubar)
index a3fba7789db60f886dfcd8ac6a094162b415ca08..53fdc86ba70d466db70ef0a62df3d9200fd03cea 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'imagesettings.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -58,7 +58,7 @@ class Ui_ImageSettingsUi(object):
         self.imageQualitySlider = QSlider(self.groupBox_2)
         self.imageQualitySlider.setObjectName(u"imageQualitySlider")
         self.imageQualitySlider.setMaximum(4)
-        self.imageQualitySlider.setOrientation(Qt.Horizontal)
+        self.imageQualitySlider.setOrientation(Qt.Orientation.Horizontal)
 
         self.gridLayout_2.addWidget(self.imageQualitySlider, 4, 1, 1, 1)
 
@@ -71,8 +71,8 @@ class Ui_ImageSettingsUi(object):
 
         self.buttonBox = QDialogButtonBox(ImageSettingsUi)
         self.buttonBox.setObjectName(u"buttonBox")
-        self.buttonBox.setOrientation(Qt.Horizontal)
-        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+        self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+        self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
 
         self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 1)
 
index eec626f27479db77feab010ade9498f98951b559..8a443b524933cb7c617faf5b3afd0c37b5207cea 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'videosettings.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -29,8 +29,8 @@ class Ui_VideoSettingsUi(object):
         self.gridLayout_3.setObjectName(u"gridLayout_3")
         self.buttonBox = QDialogButtonBox(VideoSettingsUi)
         self.buttonBox.setObjectName(u"buttonBox")
-        self.buttonBox.setOrientation(Qt.Horizontal)
-        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+        self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+        self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
 
         self.gridLayout_3.addWidget(self.buttonBox, 4, 1, 1, 1)
 
@@ -72,7 +72,7 @@ class Ui_VideoSettingsUi(object):
 
         self.fpsSlider = QSlider(self.groupBox_2)
         self.fpsSlider.setObjectName(u"fpsSlider")
-        self.fpsSlider.setOrientation(Qt.Horizontal)
+        self.fpsSlider.setOrientation(Qt.Orientation.Horizontal)
 
         self.horizontalLayout.addWidget(self.fpsSlider)
 
@@ -131,7 +131,7 @@ class Ui_VideoSettingsUi(object):
         self.qualitySlider = QSlider(self.groupBox)
         self.qualitySlider.setObjectName(u"qualitySlider")
         self.qualitySlider.setMaximum(4)
-        self.qualitySlider.setOrientation(Qt.Horizontal)
+        self.qualitySlider.setOrientation(Qt.Orientation.Horizontal)
 
         self.verticalLayout.addWidget(self.qualitySlider)
 
index 50fb8e08100fd8c679ef90d3abb526f58c1f6788..ed9b01f71e22e4e46a6c920dfe45dd2bfe128df5 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'videosettings_mobile.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -24,7 +24,7 @@ class Ui_VideoSettingsUi(object):
     def setupUi(self, VideoSettingsUi):
         if not VideoSettingsUi.objectName():
             VideoSettingsUi.setObjectName(u"VideoSettingsUi")
-        VideoSettingsUi.resize(329, 591)
+        VideoSettingsUi.resize(329, 610)
         self.gridLayout_3 = QGridLayout(VideoSettingsUi)
         self.gridLayout_3.setObjectName(u"gridLayout_3")
         self.widget = QWidget(VideoSettingsUi)
@@ -76,7 +76,7 @@ class Ui_VideoSettingsUi(object):
         self.qualitySlider = QSlider(self.groupBox)
         self.qualitySlider.setObjectName(u"qualitySlider")
         self.qualitySlider.setMaximum(4)
-        self.qualitySlider.setOrientation(Qt.Horizontal)
+        self.qualitySlider.setOrientation(Qt.Orientation.Horizontal)
 
         self.verticalLayout.addWidget(self.qualitySlider)
 
@@ -127,8 +127,8 @@ class Ui_VideoSettingsUi(object):
 
         self.buttonBox = QDialogButtonBox(self.groupBox_2)
         self.buttonBox.setObjectName(u"buttonBox")
-        self.buttonBox.setOrientation(Qt.Horizontal)
-        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+        self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+        self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
 
         self.gridLayout_2.addWidget(self.buttonBox, 7, 0, 1, 1)
 
@@ -143,7 +143,7 @@ class Ui_VideoSettingsUi(object):
 
         self.fpsSlider = QSlider(self.groupBox_2)
         self.fpsSlider.setObjectName(u"fpsSlider")
-        self.fpsSlider.setOrientation(Qt.Horizontal)
+        self.fpsSlider.setOrientation(Qt.Orientation.Horizontal)
 
         self.horizontalLayout.addWidget(self.fpsSlider)
 
index a88cb39ed20d3c2877d965f85f9e60ae86fe955d..d0485023adc3167b94779c90dd8ab8d2859f7d45 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from PySide6.QtMultimedia import (QCameraFormat, QMediaFormat, QMediaRecorder,
index 3c1f71f11cc2105132922a17014c32aa5f8e60d0..ec35e957c40f10a2d866b70157dc3e798c861f85 100644 (file)
    <item row="4" column="1">
     <widget class="QDialogButtonBox" name="buttonBox">
      <property name="orientation">
-      <enum>Qt::Horizontal</enum>
+      <enum>Qt::Orientation::Horizontal</enum>
      </property>
      <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
      </property>
     </widget>
    </item>
@@ -65,7 +65,7 @@
         <item>
          <widget class="QSlider" name="fpsSlider">
           <property name="orientation">
-           <enum>Qt::Horizontal</enum>
+           <enum>Qt::Orientation::Horizontal</enum>
           </property>
          </widget>
         </item>
             <number>4</number>
            </property>
            <property name="orientation">
-            <enum>Qt::Horizontal</enum>
+            <enum>Qt::Orientation::Horizontal</enum>
            </property>
           </widget>
          </item>
    <item row="3" column="0">
     <spacer name="verticalSpacer">
      <property name="orientation">
-      <enum>Qt::Vertical</enum>
+      <enum>Qt::Orientation::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
index 6584f07f9a8849f0b1f5b4e69db985b3777a6a11..06d20684ea266a335f815d968c8e981a42d0af87 100644 (file)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>329</width>
-    <height>591</height>
+    <height>610</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -80,7 +80,7 @@
             <number>4</number>
            </property>
            <property name="orientation">
-            <enum>Qt::Horizontal</enum>
+            <enum>Qt::Orientation::Horizontal</enum>
            </property>
           </widget>
          </item>
       <item row="7" column="0">
        <widget class="QDialogButtonBox" name="buttonBox">
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Orientation::Horizontal</enum>
         </property>
         <property name="standardButtons">
-         <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+         <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
         </property>
        </widget>
       </item>
         <item>
          <widget class="QSlider" name="fpsSlider">
           <property name="orientation">
-           <enum>Qt::Horizontal</enum>
+           <enum>Qt::Orientation::Horizontal</enum>
           </property>
          </widget>
         </item>
index d28f2887e97dcfcd792a13edbae01a24b39aa594..23fdbb4cc5518da205e41281eb4f2f69b80f48d1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 Multimedia player example"""
 
@@ -9,7 +10,7 @@ from PySide6.QtGui import QAction, QIcon, QKeySequence
 from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
                                QMainWindow, QSlider, QStyle, QToolBar)
 from PySide6.QtMultimedia import (QAudioOutput, QMediaFormat,
-                                  QMediaPlayer)
+                                  QMediaPlayer, QAudio)
 from PySide6.QtMultimediaWidgets import QVideoWidget
 
 
@@ -87,16 +88,16 @@ class MainWindow(QMainWindow):
         play_menu.addAction(self._stop_action)
 
         self._volume_slider = QSlider()
-        self._volume_slider.setOrientation(Qt.Horizontal)
+        self._volume_slider.setOrientation(Qt.Orientation.Horizontal)
         self._volume_slider.setMinimum(0)
         self._volume_slider.setMaximum(100)
         available_width = self.screen().availableGeometry().width()
         self._volume_slider.setFixedWidth(available_width / 10)
-        self._volume_slider.setValue(self._audio_output.volume())
+        self._volume_slider.setValue(self._audio_output.volume() * 100)
         self._volume_slider.setTickInterval(10)
         self._volume_slider.setTickPosition(QSlider.TicksBelow)
         self._volume_slider.setToolTip("Volume")
-        self._volume_slider.valueChanged.connect(self._audio_output.setVolume)
+        self._volume_slider.valueChanged.connect(self.setVolume)
         tool_bar.addWidget(self._volume_slider)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.HelpAbout)
@@ -183,6 +184,13 @@ class MainWindow(QMainWindow):
         print(error_string, file=sys.stderr)
         self.show_status_message(error_string)
 
+    @Slot()
+    def setVolume(self):
+        self.volumeValue = QAudio.convertVolume(self._volume_slider.value() / 100.0,
+                                                QAudio.VolumeScale.LogarithmicVolumeScale,
+                                                QAudio.VolumeScale.LinearVolumeScale)
+        self._audio_output.setVolume(self.volumeValue)
+
 
 if __name__ == '__main__':
     app = QApplication(sys.argv)
index f445bac038f9aef53d9aae5f997641a75e093f67..df639216475397c9aac23948082e3f7630f43e5f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the QtMultiMedia Screen Capture Example from Qt v6.x"""
 
index c7e0c596a3a69a38a0861ab5aded16eeb4476c96..498803f60e3f31b3e818a3157fa0de828edbf471 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import Enum, auto
 
@@ -56,7 +57,7 @@ class ScreenCapturePreview(QWidget):
         update_action = QAction("Update windows List", self)
         update_action.triggered.connect(self._window_list_model.populate)
         self._window_list_view.addAction(update_action)
-        self._window_list_view.setContextMenuPolicy(Qt.ActionsContextMenu)
+        self._window_list_view.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
 
         grid_layout = QGridLayout(self)
         grid_layout.addWidget(self._screen_label, 0, 0)
@@ -81,9 +82,9 @@ class ScreenCapturePreview(QWidget):
 
         self._start_stop_button.clicked.connect(self.on_start_stop_button_clicked)
         self._screen_capture.errorOccurred.connect(self.on_screen_capture_error_occured,
-                                                   Qt.QueuedConnection)
+                                                   Qt.ConnectionType.QueuedConnection)
         self._window_capture.errorOccurred.connect(self.on_window_capture_error_occured,
-                                                   Qt.QueuedConnection)
+                                                   Qt.ConnectionType.QueuedConnection)
         self.update_active(SourceType.Screen, True)
 
     @Slot(QItemSelection)
index 72bb306e3f69de1988c876fc1094d5b93906bb64..7e92c3a3d8b67ea4e2b1534cf7a3ae0169b17b54 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtCore import QAbstractListModel, Qt, Slot
@@ -20,7 +21,7 @@ class ScreenListModel(QAbstractListModel):
     def data(self, index, role):
         screen_list = QGuiApplication.screens()
 
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             screen = screen_list[index.row()]
             w = screen.size().width()
             h = screen.size().height()
index 079040ec2ea4bb04c07cc3e8d9fe4c461b0de49f..4f622331877a0b6abf22cd84994af15cf42e81db 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QAbstractListModel, Qt, Slot
 from PySide6.QtMultimedia import QWindowCapture
@@ -15,7 +16,7 @@ class WindowListModel(QAbstractListModel):
         return len(self._window_list)
 
     def data(self, index, role):
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             window = self._window_list[index.row()]
             return window.description()
         return None
index d0dd7e0ad632ddc76c4e20bd1776d1c76f17fd2e..73476d47372be364920916d167ffa6bfaebc4f09 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the network/blockingfortunclient example from Qt v5.x, originating from PyQt"""
 
@@ -95,10 +96,11 @@ class BlockingClient(QWidget):
         port_label = QLabel("S&erver port:")
 
         for ip_address in QNetworkInterface.allAddresses():
-            if ip_address != QHostAddress.LocalHost and ip_address.toIPv4Address() != 0:
+            if (ip_address != QHostAddress.SpecialAddress.LocalHost
+                    and ip_address.toIPv4Address() != 0):
                 break
         else:
-            ip_address = QHostAddress(QHostAddress.LocalHost)
+            ip_address = QHostAddress(QHostAddress.SpecialAddress.LocalHost)
 
         ip_address = ip_address.toString()
 
@@ -120,8 +122,8 @@ class BlockingClient(QWidget):
         quit_button = QPushButton("Quit")
 
         button_box = QDialogButtonBox()
-        button_box.addButton(self._get_fortune_button, QDialogButtonBox.ActionRole)
-        button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
+        button_box.addButton(self._get_fortune_button, QDialogButtonBox.ButtonRole.ActionRole)
+        button_box.addButton(quit_button, QDialogButtonBox.ButtonRole.RejectRole)
 
         self._get_fortune_button.clicked.connect(self.request_new_fortune)
         quit_button.clicked.connect(self.close)
@@ -157,11 +159,11 @@ class BlockingClient(QWidget):
         self._get_fortune_button.setEnabled(True)
 
     def display_error(self, socketError, message):
-        if socketError == QAbstractSocket.HostNotFoundError:
+        if socketError == QAbstractSocket.SocketError.HostNotFoundError:
             QMessageBox.information(self, "Blocking Fortune Client",
                                     "The host was not found. Please check the host and port "
                                     "settings.")
-        elif socketError == QAbstractSocket.ConnectionRefusedError:
+        elif socketError == QAbstractSocket.SocketError.ConnectionRefusedError:
             QMessageBox.information(self, "Blocking Fortune Client",
                                     "The connection was refused by the peer. Make sure the "
                                     "fortune server is running, and check that the host name "
diff --git a/examples/network/blockingfortuneclient/doc/blockingfortuneclient.png b/examples/network/blockingfortuneclient/doc/blockingfortuneclient.png
new file mode 100644 (file)
index 0000000..ab918e7
Binary files /dev/null and b/examples/network/blockingfortuneclient/doc/blockingfortuneclient.png differ
diff --git a/examples/network/blockingfortuneclient/doc/blockingfortuneclient.rst b/examples/network/blockingfortuneclient/doc/blockingfortuneclient.rst
new file mode 100644 (file)
index 0000000..661153d
--- /dev/null
@@ -0,0 +1,11 @@
+Blocking Fortune Client Example
+===============================
+
+Demonstrates how to create a client for a network service. It corresponds to the
+Qt example `Blocking Fortune Client Example
+<https://doc.qt.io/qt-6/qtnetwork-blockingfortuneclient-example.html>`_.
+
+.. image:: blockingfortuneclient.png
+    :align: center
+    :alt: blockingfortuneclient screenshot
+    :width: 400
index fba0cb980910ff4131bac1663541440470972e64..97e0e3e234a479e0e417bad39144ea8bff9fb98a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import (
     QWidget,
@@ -35,8 +36,8 @@ class DownloaderWidget(QWidget):
         self.link_box.setPlaceholderText("Download Link ...")
 
         self._open_folder_action = self.dest_box.addAction(
-            qApp.style().standardIcon(QStyle.SP_DirOpenIcon),  # noqa: F821
-            QLineEdit.TrailingPosition
+            qApp.style().standardIcon(QStyle.StandardPixmap.SP_DirOpenIcon),  # noqa: F821
+            QLineEdit.ActionPosition.TrailingPosition
         )
         self._open_folder_action.triggered.connect(self.on_open_folder)
 
@@ -53,7 +54,7 @@ class DownloaderWidget(QWidget):
         # Â Default destination dir
         self.dest_box.setText(
             QDir.fromNativeSeparators(
-                QStandardPaths.writableLocation(QStandardPaths.DownloadLocation)
+                QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DownloadLocation)
             )
         )
 
@@ -104,7 +105,7 @@ class DownloaderWidget(QWidget):
         # Create the file in write mode to append bytes
         self.file = QSaveFile(dest_file)
 
-        if self.file.open(QIODevice.WriteOnly):
+        if self.file.open(QIODevice.OpenModeFlag.WriteOnly):
 
             # Start a GET HTTP request
             self.reply = self.manager.get(QNetworkRequest(url_file))
@@ -132,7 +133,7 @@ class DownloaderWidget(QWidget):
     def on_ready_read(self):
         """ Get available bytes and store them into the file"""
         if self.reply:
-            if self.reply.error() == QNetworkReply.NoError:
+            if self.reply.error() == QNetworkReply.NetworkError.NoError:
                 self.file.write(self.reply.readAll())
 
     @Slot()
@@ -162,7 +163,7 @@ class DownloaderWidget(QWidget):
     def on_open_folder(self):
 
         dir_path = QFileDialog.getExistingDirectory(
-            self, "Open Directory", QDir.homePath(), QFileDialog.ShowDirsOnly
+            self, "Open Directory", QDir.homePath(), QFileDialog.Option.ShowDirsOnly
         )
 
         if dir_path:
diff --git a/examples/network/fortuneclient/doc/fortuneclient.png b/examples/network/fortuneclient/doc/fortuneclient.png
new file mode 100644 (file)
index 0000000..cae0b06
Binary files /dev/null and b/examples/network/fortuneclient/doc/fortuneclient.png differ
diff --git a/examples/network/fortuneclient/doc/fortuneclient.rst b/examples/network/fortuneclient/doc/fortuneclient.rst
new file mode 100644 (file)
index 0000000..e9719c8
--- /dev/null
@@ -0,0 +1,11 @@
+Fortune Client Example
+======================
+
+Demonstrates how to create a client for a network service. It corresponds to the
+Qt example `Fortune Client Example
+<https://doc.qt.io/qt-6/qtnetwork-fortuneclient-example.html>`_.
+
+.. image:: fortuneclient.png
+    :align: center
+    :alt: fortuneclient screenshot
+    :width: 400
index e88e5e35b132b244336936f00a6a82e1b75c6d0e..54ce71f4e63b9908783c8f93940fd4b96c5b33b2 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the network/fortuneclient example from Qt v5.x"""
 
@@ -40,8 +41,8 @@ class Client(QDialog):
         quit_button = QPushButton("Quit")
 
         button_box = QDialogButtonBox()
-        button_box.addButton(self._get_fortune_button, QDialogButtonBox.ActionRole)
-        button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
+        button_box.addButton(self._get_fortune_button, QDialogButtonBox.ButtonRole.ActionRole)
+        button_box.addButton(quit_button, QDialogButtonBox.ButtonRole.RejectRole)
 
         self._tcp_socket = QTcpSocket(self)
 
@@ -72,7 +73,7 @@ class Client(QDialog):
 
     def read_fortune(self):
         instr = QDataStream(self._tcp_socket)
-        instr.setVersion(QDataStream.Qt_4_0)
+        instr.setVersion(QDataStream.Version.Qt_4_0)
 
         if self._block_size == 0:
             if self._tcp_socket.bytesAvailable() < 2:
@@ -94,13 +95,13 @@ class Client(QDialog):
         self._get_fortune_button.setEnabled(True)
 
     def display_error(self, socketError):
-        if socketError == QAbstractSocket.RemoteHostClosedError:
+        if socketError == QAbstractSocket.SocketError.RemoteHostClosedError:
             pass
-        elif socketError == QAbstractSocket.HostNotFoundError:
+        elif socketError == QAbstractSocket.SocketError.HostNotFoundError:
             QMessageBox.information(self, "Fortune Client",
                                     "The host was not found. Please check the host name and "
                                     "port settings.")
-        elif socketError == QAbstractSocket.ConnectionRefusedError:
+        elif socketError == QAbstractSocket.SocketError.ConnectionRefusedError:
             QMessageBox.information(self, "Fortune Client",
                                     "The connection was refused by the peer. Make sure the "
                                     "fortune server is running, and check that the host name "
diff --git a/examples/network/fortuneserver/doc/fortuneserver.png b/examples/network/fortuneserver/doc/fortuneserver.png
new file mode 100644 (file)
index 0000000..55ea8b1
Binary files /dev/null and b/examples/network/fortuneserver/doc/fortuneserver.png differ
diff --git a/examples/network/fortuneserver/doc/fortuneserver.rst b/examples/network/fortuneserver/doc/fortuneserver.rst
new file mode 100644 (file)
index 0000000..fd21340
--- /dev/null
@@ -0,0 +1,11 @@
+Fortune Server Example
+======================
+
+Demonstrates how to create a server for a network service. It corresponds to the
+Qt example `Fortune Server Example
+<https://doc.qt.io/qt-6/qtnetwork-fortuneserver-example.html>`_.
+
+.. image:: fortuneserver.png
+    :align: center
+    :alt: fortuneserver screenshot
+    :width: 400
index a94a49f4204fe15e5f3313acb7757a2819181996..232485eefa5baefe4b69fdd9ed84d959ba9c65ee 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the network/fortuneserver example from Qt v5.x"""
 
@@ -19,7 +20,7 @@ class Server(QDialog):
         super().__init__(parent)
 
         status_label = QLabel()
-        status_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
+        status_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
         quit_button = QPushButton("Quit")
         quit_button.setAutoDefault(False)
 
@@ -59,8 +60,8 @@ class Server(QDialog):
 
     def send_fortune(self):
         block = QByteArray()
-        out = QDataStream(block, QIODevice.WriteOnly)
-        out.setVersion(QDataStream.Qt_4_0)
+        out = QDataStream(block, QIODevice.OpenModeFlag.WriteOnly)
+        out.setVersion(QDataStream.Version.Qt_4_0)
         out.writeUInt16(0)
         fortune = self.fortunes[random.randint(0, len(self.fortunes) - 1)]
 
index d3fc67c05d52d148635e28d65fad6d8a2c1d354a..bb19647e6a6a7fb33065aa7ff827b3edfe85952d 100644 (file)
@@ -1,14 +1,13 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from typing import List
+from __future__ import annotations
 
 from PySide6.QtCore import (QByteArray, QEvent, QObject, QPoint, Qt, QTimer,
                             QXmlStreamReader, Slot)
 from PySide6.QtGui import QPalette
 from PySide6.QtNetwork import (QNetworkAccessManager, QNetworkReply,
                                QNetworkRequest)
-from PySide6.QtWidgets import QFrame, QTreeWidget, QTreeWidgetItem
+from PySide6.QtWidgets import QAbstractItemView, QFrame, QTreeWidget, QTreeWidgetItem
 
 
 class GSuggestCompletion(QObject):
@@ -16,18 +15,18 @@ class GSuggestCompletion(QObject):
         super().__init__(parent)
         self.editor = parent
         self.popup = QTreeWidget()
-        self.popup.setWindowFlags(Qt.Popup)
-        self.popup.setFocusPolicy(Qt.NoFocus)
+        self.popup.setWindowFlags(Qt.WindowType.Popup)
+        self.popup.setFocusPolicy(Qt.FocusPolicy.NoFocus)
         self.popup.setFocusProxy(parent)
         self.popup.setMouseTracking(True)
 
         self.popup.setColumnCount(1)
         self.popup.setUniformRowHeights(True)
         self.popup.setRootIsDecorated(False)
-        self.popup.setEditTriggers(QTreeWidget.NoEditTriggers)
-        self.popup.setSelectionBehavior(QTreeWidget.SelectRows)
-        self.popup.setFrameStyle(QFrame.Box | QFrame.Plain)
-        self.popup.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+        self.popup.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+        self.popup.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
+        self.popup.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Plain)
+        self.popup.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
         self.popup.header().hide()
 
         self.popup.installEventFilter(self)
@@ -46,28 +45,28 @@ class GSuggestCompletion(QObject):
     def eventFilter(self, obj: QObject, ev: QEvent):
         if obj is not self.popup:
             return False
-        if ev.type() == QEvent.MouseButtonPress:
+        if ev.type() == QEvent.Type.MouseButtonPress:
             self.popup.hide()
             self.editor.setFocus()
             return True
 
-        if ev.type() == QEvent.KeyPress:
+        if ev.type() == QEvent.Type.KeyPress:
             consumed = False
             key = ev.key()
-            if key in (Qt.Key_Enter, Qt.Key_Return):
+            if key in (Qt.Key.Key_Enter, Qt.Key.Key_Return):
                 self.done_completion()
                 consumed = True
-            elif key == Qt.Key_Escape:
+            elif key == Qt.Key.Key_Escape:
                 self.editor.setFocus()
                 self.popup.hide()
                 consumed = True
             elif key in (
-                Qt.Key_Up,
-                Qt.Key_Down,
-                Qt.Key_Home,
-                Qt.Key_End,
-                Qt.Key_PageUp,
-                Qt.Key_PageDown,
+                Qt.Key.Key_Up,
+                Qt.Key.Key_Down,
+                Qt.Key.Key_Home,
+                Qt.Key.Key_End,
+                Qt.Key.Key_PageUp,
+                Qt.Key.Key_PageDown,
             ):
                 pass
             else:
@@ -77,11 +76,11 @@ class GSuggestCompletion(QObject):
             return consumed
         return False
 
-    def show_completion(self, choices: List[str]):
+    def show_completion(self, choices: list[str]):
         if not choices:
             return
         pal = self.editor.palette()
-        color = pal.color(QPalette.Disabled, QPalette.WindowText)
+        color = pal.color(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText)
 
         self.popup.setUpdatesEnabled(False)
         self.popup.clear()
@@ -120,14 +119,14 @@ class GSuggestCompletion(QObject):
 
     @Slot(QNetworkReply)
     def handle_network_data(self, network_reply: QNetworkReply):
-        if network_reply.error() == QNetworkReply.NoError:
-            choices: List[str] = []
+        if network_reply.error() == QNetworkReply.NetworkError.NoError:
+            choices: list[str] = []
 
             response: QByteArray = network_reply.readAll()
             xml = QXmlStreamReader(str(response))
             while not xml.atEnd():
                 xml.readNext()
-                if xml.tokenType() == QXmlStreamReader.StartElement:
+                if xml.tokenType() == QXmlStreamReader.TokenType.StartElement:
                     if xml.name() == "suggestion":
                         s = xml.attributes().value("data")
                         choices.append(s)
index 1efda08e66724b5167a3888622999802422efb6b..50510421fac131fbe74512bbf2ae104169f577f1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 9cbe20b23122bef7afa5f931ebc810ae74fc51e9..a713a62341771c9195077a770b675248ba9450d5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtGui import QDesktopServices
index 673afce286c695528ff9600ec4296f305980285f..0790ff5ce8bcdc314eb865ff16de67e7249213a2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QByteArray, Qt
 from PySide6.QtGui import QGuiApplication
@@ -29,8 +30,8 @@ class Dialog(QDialog):
         self.quit_button = QPushButton("&Quit")
 
         self.button_box = QDialogButtonBox()
-        self.button_box.addButton(self.start_button, QDialogButtonBox.ActionRole)
-        self.button_box.addButton(self.quit_button, QDialogButtonBox.RejectRole)
+        self.button_box.addButton(self.start_button, QDialogButtonBox.ButtonRole.ActionRole)
+        self.button_box.addButton(self.quit_button, QDialogButtonBox.ButtonRole.RejectRole)
 
         self.start_button.clicked.connect(self.start)
         self.quit_button.clicked.connect(self.close)
@@ -58,7 +59,7 @@ class Dialog(QDialog):
 
         self.start_button.setEnabled(False)
 
-        QGuiApplication.setOverrideCursor(Qt.WaitCursor)
+        QGuiApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
 
         self.bytes_written = 0
         self.bytes_received = 0
@@ -68,14 +69,15 @@ class Dialog(QDialog):
                 self,
                 "Loopback",
                 f"Unable to start the test {self.tcp_server.errorString()}",
-                QMessageBox.Retry | QMessageBox.Cancel,
+                QMessageBox.StandardButton.Retry | QMessageBox.StandardButton.Cancel,
             )
-            if ret == QMessageBox.Cancel:
+            if ret == QMessageBox.StandardButton.Cancel:
                 return
 
         self.server_status_label.setText("Listening")
         self.client_status_label.setText("Connecting")
-        self.tcp_client.connectToHost(QHostAddress.LocalHost, self.tcp_server.serverPort())
+        self.tcp_client.connectToHost(QHostAddress.SpecialAddress.LocalHost,
+                                      self.tcp_server.serverPort())
 
     def accept_connection(self):
 
@@ -131,7 +133,7 @@ class Dialog(QDialog):
         self.client_status_label.setText(f"Sent {self.bytes_written / (1024 ** 2)} MB")
 
     def display_error(self, socket_error: QAbstractSocket.SocketError):
-        if socket_error == QAbstractSocket.RemoteHostClosedError:
+        if socket_error == QAbstractSocket.SocketError.RemoteHostClosedError:
             return
 
         QMessageBox.information(
index d684b6babef25eb9d533ab0fe57dfe4c4328b77b..046a8fbac6ac8019429af3103d5ca918fd3b5997 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
diff --git a/examples/network/threadedfortuneserver/doc/threadedfortuneserver.png b/examples/network/threadedfortuneserver/doc/threadedfortuneserver.png
new file mode 100644 (file)
index 0000000..72315e0
Binary files /dev/null and b/examples/network/threadedfortuneserver/doc/threadedfortuneserver.png differ
diff --git a/examples/network/threadedfortuneserver/doc/threadedfortuneserver.rst b/examples/network/threadedfortuneserver/doc/threadedfortuneserver.rst
new file mode 100644 (file)
index 0000000..3f66066
--- /dev/null
@@ -0,0 +1,12 @@
+Threaded Fortune Server Example
+===============================
+
+The Threaded Fortune Server example shows how to create a server for a simple
+network service that uses threads to handle requests from different clients.
+It corresponds to the Qt example `Threaded Fortune Server Example
+<https://doc.qt.io/qt-6/qtnetwork-threadedfortuneserver-example.html>`_.
+
+.. image:: threadedfortuneserver.png
+    :align: center
+    :alt: threadedfortuneserver screenshot
+    :width: 400
index c75e2bc572df15040bcaf5e66b904e5a2fe011b3..01ae0621f3e12423f7b2b193ac2e2369181646af 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the network/threadedfortuneserver example from Qt v5.x, originating from PyQt"""
 
@@ -30,8 +31,8 @@ class FortuneThread(QThread):
             return
 
         block = QByteArray()
-        outstr = QDataStream(block, QIODevice.WriteOnly)
-        outstr.setVersion(QDataStream.Qt_4_0)
+        outstr = QDataStream(block, QIODevice.OpenModeFlag.WriteOnly)
+        outstr.setVersion(QDataStream.Version.Qt_4_0)
         outstr.writeUInt16(0)
         outstr.writeQString(self.text)
         outstr.device().seek(0)
@@ -67,7 +68,7 @@ class Dialog(QDialog):
         self.server = FortuneServer()
 
         status_label = QLabel()
-        status_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
+        status_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
         status_label.setWordWrap(True)
         quit_button = QPushButton("Quit")
         quit_button.setAutoDefault(False)
@@ -80,10 +81,11 @@ class Dialog(QDialog):
             return
 
         for ip_address in QNetworkInterface.allAddresses():
-            if ip_address != QHostAddress.LocalHost and ip_address.toIPv4Address() != 0:
+            if (ip_address != QHostAddress.SpecialAddress.LocalHost
+                    and ip_address.toIPv4Address() != 0):
                 break
         else:
-            ip_address = QHostAddress(QHostAddress.LocalHost)
+            ip_address = QHostAddress(QHostAddress.SpecialAddress.LocalHost)
 
         ip_address = ip_address.toString()
         port = self.server.serverPort()
index e88e32cc5ed29b98380e5c8b001639c6b6a9c725..c3e3694d15e84c6d9a5d04363195aed4e4bf80fe 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the networkauth redditclient example from Qt v6.x"""
 
index fd6f9bc97ad17cc21a2f3d28054f4e484ee5f9c4..f2503f19cb17c4136027eecc17b44ed21e2728e8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import functools
 from PySide6.QtCore import (QAbstractTableModel, QJsonDocument, QModelIndex,
@@ -30,7 +31,7 @@ class RedditModel(QAbstractTableModel):
     def data(self, index, role):
         if not index.isValid():
             return None
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             children_object = self._threads[index.row()]
             data_object = children_object["data"]
             return data_object["title"]
index ed4c3d2c7f76015ecc2ed4597dbb1acaf6756409..80dfb9448f9a930abb9eada28a254432a028c38d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import functools
 
index 311d5b76511d4bca0d8e74163745c00eb382fc7a..9770be63419a2b07cc50c5539a5b981551b0b041 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the opengl/contextinfo example from Qt v5.x"""
 
@@ -73,7 +74,7 @@ colors = numpy.array([1, 0, 0, 0, 1, 0, 0, 0, 1], dtype=numpy.float32)
 
 
 def print_surface_format(surface_format):
-    if surface_format.profile() == QSurfaceFormat.CoreProfile:
+    if surface_format.profile() == QSurfaceFormat.OpenGLContextProfile.CoreProfile:
         profile_name = 'core'
     else:
         profile_name = 'compatibility'
@@ -85,7 +86,7 @@ def print_surface_format(surface_format):
 class RenderWindow(QWindow):
     def __init__(self, fmt):
         super().__init__()
-        self.setSurfaceType(QWindow.OpenGLSurface)
+        self.setSurfaceType(QWindow.SurfaceType.OpenGLSurface)
         self.setFormat(fmt)
         self.context = QOpenGLContext(self)
         self.context.setFormat(self.requestedFormat())
@@ -101,22 +102,22 @@ class RenderWindow(QWindow):
         self.vbo = QOpenGLBuffer()
 
         fmt = self.context.format()
-        use_new_style_shader = fmt.profile() == QSurfaceFormat.CoreProfile
+        use_new_style_shader = fmt.profile() == QSurfaceFormat.OpenGLContextProfile.CoreProfile
         # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile
         # concept 3.2+ has. This may still fail since version 150 (3.2) is
         # specified in the sources but it's worth a try.
-        if (fmt.renderableType() == QSurfaceFormat.OpenGL and fmt.majorVersion() == 3
+        if (fmt.renderableType() == QSurfaceFormat.RenderableType.OpenGL and fmt.majorVersion() == 3
                 and fmt.minorVersion() <= 1):
-            use_new_style_shader = not fmt.testOption(QSurfaceFormat.DeprecatedFunctions)
+            use_new_style_shader = not fmt.testOption(QSurfaceFormat.FormatOption.DeprecatedFunctions)  # noqa: E501
 
         vertex_shader = vertex_shader_source if use_new_style_shader else vertex_shader_source_110
         fragment_shader = (fragment_shader_source
                            if use_new_style_shader
                            else fragment_shader_source_110)
-        if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertex_shader):
+        if not self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertex_shader):  # noqa: E501
             log = self.program.log()
             raise Exception("Vertex shader could not be added: {log} ({vertexShader})")
-        if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragment_shader):
+        if not self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragment_shader):  # noqa: E501
             log = self.program.log()
             raise Exception(f"Fragment shader could not be added: {log} ({fragment_shader})")
         if not self.program.link():
@@ -253,11 +254,11 @@ if __name__ == '__main__':
                         help='Use Desktop OpenGL')
     options = parser.parse_args()
     if options.gles:
-        QCoreApplication.setAttribute(Qt.AA_UseOpenGLES)
+        QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseOpenGLES)
     elif options.software:
-        QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL)
+        QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL)
     elif options.desktop:
-        QCoreApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
+        QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
 
     app = QApplication(sys.argv)
     main_window = MainWindow()
diff --git a/examples/opengl/contextinfo/requirements.txt b/examples/opengl/contextinfo/requirements.txt
new file mode 100644 (file)
index 0000000..3727213
--- /dev/null
@@ -0,0 +1 @@
+PyOpenGL
index bbf200a6b4bd36745cebe488d780f2ce30a83a41..9660f87365fbb6f3098440d415fcf5bb0963802b 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # Copyright (C) 2013 Riverbank Computing Limited.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import ctypes
 from PySide6.QtCore import Signal, Slot, Qt, QSize, QPointF
@@ -81,7 +82,7 @@ class GLWidget(QOpenGLWidget, QOpenGLFunctions):
         QOpenGLWidget.__init__(self, parent)
         QOpenGLFunctions.__init__(self)
 
-        self._core = QSurfaceFormat.defaultFormat().profile() == QSurfaceFormat.CoreProfile
+        self._core = QSurfaceFormat.defaultFormat().profile() == QSurfaceFormat.OpenGLContextProfile.CoreProfile  # noqa: E501
 
         self._x_rot = 0
         self._y_rot = 0
@@ -179,9 +180,9 @@ class GLWidget(QOpenGLWidget, QOpenGLFunctions):
             self._vertex_shader = VERTEX_SHADER_SOURCE
             self._fragment_shader = FRAGMENT_SHADER_SOURCE
 
-        self.program.addShaderFromSourceCode(QOpenGLShader.Vertex,
+        self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex,
                                              self._vertex_shader)
-        self.program.addShaderFromSourceCode(QOpenGLShader.Fragment,
+        self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment,
                                              self._fragment_shader)
         self.program.bindAttributeLocation("vertex", 0)
         self.program.bindAttributeLocation("normal", 1)
@@ -262,7 +263,7 @@ class GLWidget(QOpenGLWidget, QOpenGLFunctions):
         dx = pos.x() - self._last_pos.x()
         dy = pos.y() - self._last_pos.y()
 
-        if event.buttons() & Qt.LeftButton:
+        if event.buttons() & Qt.MouseButton.LeftButton:
             self.set_xrotation(self._x_rot + 8 * dy)
             self.set_yrotation(self._y_rot + 8 * dx)
         elif event.buttons() & Qt.RightButton:
index c236a1ec96e810528935a9fc810e3e8745b5c928..3be1b5bdac25051d4c743cd6bd62bc0b3df223b5 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # Copyright (C) 2013 Riverbank Computing Limited.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 
index c7eb78a82bbf3d63ed4469bfa35ed91ef7cd0135..6bce36b017d66f130c41b35c149478fb3f81357c 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # Copyright (C) 2013 Riverbank Computing Limited.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the opengl/hellogl2 example from Qt v6.x"""
 
@@ -16,9 +17,9 @@ try:
     from glwidget import GLWidget
 except ImportError:
     app = QApplication(sys.argv)
-    message_box = QMessageBox(QMessageBox.Critical, "OpenGL hellogl",
+    message_box = QMessageBox(QMessageBox.Icon.Critical, "OpenGL hellogl",
                               "PyOpenGL must be installed to run this example.",
-                              QMessageBox.Close)
+                              QMessageBox.StandardButton.Close)
     message_box.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate")
     message_box.exec()
     sys.exit(1)
@@ -42,15 +43,15 @@ if __name__ == '__main__':
         fmt.setSamples(4)
     if options.coreprofile:
         fmt.setVersion(3, 2)
-        fmt.setProfile(QSurfaceFormat.CoreProfile)
+        fmt.setProfile(QSurfaceFormat.OpenGLContextProfile.CoreProfile)
     QSurfaceFormat.setDefaultFormat(fmt)
 
     GLWidget.set_transparent(options.transparent)
 
     main_window = MainWindow()
     if options.transparent:
-        main_window.setAttribute(Qt.WA_TranslucentBackground)
-        main_window.setAttribute(Qt.WA_NoSystemBackground, False)
+        main_window.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
+        main_window.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground, False)
 
     main_window.show()
 
index 69b9b66fee40929e95b9ba29f586e9dafb1e718a..ce00b66f5b402be07c1bbecfb8ebb910288ea986 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot, Qt
 from PySide6.QtGui import QKeySequence
@@ -13,9 +14,9 @@ class MainWindow(QMainWindow):
     def __init__(self):
         super().__init__()
         menuWindow = self.menuBar().addMenu("Window")
-        menuWindow.addAction("Add new", QKeySequence(Qt.CTRL | Qt.Key_N),
+        menuWindow.addAction("Add new", QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_N),
                              self.onAddNew)
-        menuWindow.addAction("Quit", QKeySequence(Qt.CTRL | Qt.Key_Q),
+        menuWindow.addAction("Quit", QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q),
                              qApp.closeAllWindows)  # noqa: F821
 
         self.onAddNew()
diff --git a/examples/opengl/hellogl2/requirements.txt b/examples/opengl/hellogl2/requirements.txt
new file mode 100644 (file)
index 0000000..3727213
--- /dev/null
@@ -0,0 +1 @@
+PyOpenGL
index ad61d2f97bcd561a0481b33c23684c596e31ed9b..5d2c22ff30ce102f0ef0bac75f938f6c0c8a1d0e 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # Copyright (C) 2013 Riverbank Computing Limited.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot, Qt
 from PySide6.QtWidgets import (QHBoxLayout, QMainWindow,
@@ -58,13 +59,13 @@ class Window(QWidget):
         self.setWindowTitle(self.tr("Hello GL"))
 
     def create_slider(self):
-        slider = QSlider(Qt.Vertical)
+        slider = QSlider(Qt.Orientation.Vertical)
 
         slider.setRange(0, 360 * 16)
         slider.setSingleStep(16)
         slider.setPageStep(15 * 16)
         slider.setTickInterval(15 * 16)
-        slider.setTickPosition(QSlider.TicksRight)
+        slider.setTickPosition(QSlider.TickPosition.TicksRight)
         return slider
 
     def closeEvent(self, event):
@@ -72,7 +73,7 @@ class Window(QWidget):
         event.accept()
 
     def keyPressEvent(self, event):
-        if self.isWindow() and event.key() == Qt.Key_Escape:
+        if self.isWindow() and event.key() == Qt.Key.Key_Escape:
             self.close()
         else:
             super().keyPressEvent(event)
@@ -95,13 +96,13 @@ class Window(QWidget):
                                     "Main window already occupied")
             return
 
-        self.setAttribute(Qt.WA_DeleteOnClose, False)
+        self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
         self._dock_btn.setText("Undock")
         mainWindow.setCentralWidget(self)
 
     def undock(self):
         self.setParent(None)
-        self.setAttribute(Qt.WA_DeleteOnClose)
+        self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
         geometry = self.screen().availableGeometry()
         x = geometry.x() + (geometry.width() - self.width()) / 2
         y = geometry.y() + (geometry.height() - self.height()) / 2
diff --git a/examples/opengl/textures/requirements.txt b/examples/opengl/textures/requirements.txt
new file mode 100644 (file)
index 0000000..3727213
--- /dev/null
@@ -0,0 +1 @@
+PyOpenGL
index 87c1164b7aaaf8362bdb76b4ef8f20f94c81088e..5df84ad4f4c2296f4b54759ecabd25245b29a665 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the opengl/textures example from Qt v6.x showing the use
    of legacy OpenGL functions with QOpenGLVersionFunctionsFactory."""
@@ -45,7 +46,7 @@ class GLWidget(QOpenGLWidget):
     def __init__(self, parent):
         super().__init__(parent)
 
-        self.clearColor = Qt.black
+        self.clearColor = Qt.GlobalColor.black
         self.xRot = 0
         self.yRot = 0
         self.zRot = 0
@@ -78,7 +79,7 @@ class GLWidget(QOpenGLWidget):
     def initializeGL(self):
         profile = QOpenGLVersionProfile()
         profile.setVersion(3, 2)
-        profile.setProfile(QSurfaceFormat.CompatibilityProfile)
+        profile.setProfile(QSurfaceFormat.OpenGLContextProfile.CompatibilityProfile)
         self.funcs = QOpenGLVersionFunctionsFactory.get(profile)
         self.funcs.initializeOpenGLFunctions()
 
@@ -124,7 +125,7 @@ class GLWidget(QOpenGLWidget):
         dx = pos.x() - self.lastPos.x()
         dy = pos.y() - self.lastPos.y()
 
-        if event.buttons() & Qt.LeftButton:
+        if event.buttons() & Qt.MouseButton.LeftButton:
             self.rotateBy(8 * dy, 8 * dx, 0)
         elif event.buttons() & Qt.RightButton:
             self.rotateBy(8 * dy, 0, 8 * dx)
index edb88e77c1d304c9400c44a10f6496b232bb5036..70efc28147fa75b21d19981eecdf8f0756bfa13b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtOpenGLWidgets import QOpenGLWidget
index 2a379da27cc6e64e7c0bcddeb03699b8fca8b1e6..302ac438f418e9877d74286d1277b160c7c6adaa 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Threaded QOpenGLWidget Example from Qt v6.x"""
 
@@ -16,9 +17,9 @@ try:
 except ImportError:
     app = QApplication(sys.argv)
     message = "PyOpenGL must be installed to run this example."
-    message_box = QMessageBox(QMessageBox.Critical,
+    message_box = QMessageBox(QMessageBox.Icon.Critical,
                               "Threaded QOpenGLWidget Example",
-                              message, QMessageBox.Close)
+                              message, QMessageBox.StandardButton.Close)
     detail = "Run:\npip install PyOpenGL PyOpenGL_accelerate"
     message_box.setDetailedText(detail)
     message_box.exec()
@@ -69,9 +70,9 @@ if __name__ == "__main__":
     top_gl_widget.setToolTip(tool_tip)
     print(tool_tip)
 
-    close_shortcut = QShortcut(Qt.CTRL | Qt.Key_Q, top_gl_widget)
+    close_shortcut = QShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Q, top_gl_widget)
     close_shortcut.activated.connect(QApplication.closeAllWindows)
-    close_shortcut.setContext(Qt.ApplicationShortcut)
+    close_shortcut.setContext(Qt.ShortcutContext.ApplicationShortcut)
 
     mw1 = None
     mw2 = None
index 4999b799db5448d1a353829b776590560671cb4f..cf6dbeecc848489f9804e31acc88fbb8df40b2c8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QWidget, QHBoxLayout
 
index 81ec63cbb1e6ab63c5a1dcf9be62eb450c965a6d..b20cbe1e9d50613d68cd159f326711d9e8fc6cc6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import ctypes
 import math
@@ -145,10 +146,10 @@ class Renderer(QObject, QOpenGLFunctions):
             QMetaObject.invokeMethod(self._glwidget, "update")
 
     def _init_gl(self):
-        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
+        vshader = QOpenGLShader(QOpenGLShader.ShaderTypeBit.Vertex, self)
         vshader.compileSourceCode(VERTEX_SHADER)
 
-        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
+        fshader = QOpenGLShader(QOpenGLShader.ShaderTypeBit.Fragment, self)
         fshader.compileSourceCode(FRAGMENT_SHADER)
 
         self._program.addShader(vshader)
diff --git a/examples/opengl/threadedqopenglwidget/requirements.txt b/examples/opengl/threadedqopenglwidget/requirements.txt
new file mode 100644 (file)
index 0000000..3727213
--- /dev/null
@@ -0,0 +1 @@
+PyOpenGL
index ee7b882c22b1ccb516cece4004c16eb66a67cca0..388c590c31fc7a2ac3c4e132dc5022f362f5e841 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 import sys
index c4765fddaf3b22a49a55d1816b9023d0d9ed0b14..55ac454edc802119bcb0427a98148e6826a32523 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from argparse import ArgumentParser, RawTextHelpFormatter
index d37475aca548f84ba7889bb4b93023669169cdc6..154c877065bc8e01b8dc0851b8e981056a2e235d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 import sys
index 7cfcde4b3e8789d2c00124aae155a0a8dcf4993e..3bf46887cda233ddd4d8e13d197e0b5169b76db3 100644 (file)
@@ -54,7 +54,7 @@
        <item>
         <widget class="QSplitter" name="splitter">
          <property name="orientation">
-          <enum>Qt::Horizontal</enum>
+          <enum>Qt::Orientation::Horizontal</enum>
          </property>
          <widget class="QTabWidget" name="tabWidget">
           <property name="sizePolicy">
@@ -64,7 +64,7 @@
            </sizepolicy>
           </property>
           <property name="tabPosition">
-           <enum>QTabWidget::West</enum>
+           <enum>QTabWidget::TabPosition::West</enum>
           </property>
           <property name="currentIndex">
            <number>0</number>
      <x>0</x>
      <y>0</y>
      <width>700</width>
-     <height>23</height>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">
index 9ed525dbf13b526919a75b1bff17b6d06b2780f1..c31da6ff0adf42455ec272f05a228d6573d717b8 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'mainwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -36,7 +36,7 @@ class Ui_MainWindow(object):
         if QIcon.hasThemeIcon(iconThemeName):
             icon = QIcon.fromTheme(iconThemeName)
         else:
-            icon.addFile(u":/icons/images/document-open.svgz", QSize(), QIcon.Normal, QIcon.Off)
+            icon.addFile(u":/icons/images/document-open.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
 
         self.actionOpen.setIcon(icon)
         self.actionQuit = QAction(MainWindow)
@@ -56,7 +56,7 @@ class Ui_MainWindow(object):
         if QIcon.hasThemeIcon(iconThemeName):
             icon3 = QIcon.fromTheme(iconThemeName)
         else:
-            icon3.addFile(u":/icons/images/zoom-in.svgz", QSize(), QIcon.Normal, QIcon.Off)
+            icon3.addFile(u":/icons/images/zoom-in.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
 
         self.actionZoom_In.setIcon(icon3)
         self.actionZoom_Out = QAction(MainWindow)
@@ -66,7 +66,7 @@ class Ui_MainWindow(object):
         if QIcon.hasThemeIcon(iconThemeName):
             icon4 = QIcon.fromTheme(iconThemeName)
         else:
-            icon4.addFile(u":/icons/images/zoom-out.svgz", QSize(), QIcon.Normal, QIcon.Off)
+            icon4.addFile(u":/icons/images/zoom-out.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
 
         self.actionZoom_Out.setIcon(icon4)
         self.actionPrevious_Page = QAction(MainWindow)
@@ -76,7 +76,7 @@ class Ui_MainWindow(object):
         if QIcon.hasThemeIcon(iconThemeName):
             icon5 = QIcon.fromTheme(iconThemeName)
         else:
-            icon5.addFile(u":/icons/images/go-previous-view-page.svgz", QSize(), QIcon.Normal, QIcon.Off)
+            icon5.addFile(u":/icons/images/go-previous-view-page.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
 
         self.actionPrevious_Page.setIcon(icon5)
         self.actionNext_Page = QAction(MainWindow)
@@ -86,7 +86,7 @@ class Ui_MainWindow(object):
         if QIcon.hasThemeIcon(iconThemeName):
             icon6 = QIcon.fromTheme(iconThemeName)
         else:
-            icon6.addFile(u":/icons/images/go-next-view-page.svgz", QSize(), QIcon.Normal, QIcon.Off)
+            icon6.addFile(u":/icons/images/go-next-view-page.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
 
         self.actionNext_Page.setIcon(icon6)
         self.actionContinuous = QAction(MainWindow)
@@ -96,13 +96,13 @@ class Ui_MainWindow(object):
         self.actionBack.setObjectName(u"actionBack")
         self.actionBack.setEnabled(False)
         icon7 = QIcon()
-        icon7.addFile(u":/icons/images/go-previous-view.svgz", QSize(), QIcon.Normal, QIcon.Off)
+        icon7.addFile(u":/icons/images/go-previous-view.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionBack.setIcon(icon7)
         self.actionForward = QAction(MainWindow)
         self.actionForward.setObjectName(u"actionForward")
         self.actionForward.setEnabled(False)
         icon8 = QIcon()
-        icon8.addFile(u":/icons/images/go-next-view.svgz", QSize(), QIcon.Normal, QIcon.Off)
+        icon8.addFile(u":/icons/images/go-next-view.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionForward.setIcon(icon8)
         self.centralWidget = QWidget(MainWindow)
         self.centralWidget.setObjectName(u"centralWidget")
@@ -120,7 +120,7 @@ class Ui_MainWindow(object):
         self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
         self.splitter = QSplitter(self.widget)
         self.splitter.setObjectName(u"splitter")
-        self.splitter.setOrientation(Qt.Horizontal)
+        self.splitter.setOrientation(Qt.Orientation.Horizontal)
         self.tabWidget = QTabWidget(self.splitter)
         self.tabWidget.setObjectName(u"tabWidget")
         sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
@@ -128,7 +128,7 @@ class Ui_MainWindow(object):
         sizePolicy.setVerticalStretch(0)
         sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
         self.tabWidget.setSizePolicy(sizePolicy)
-        self.tabWidget.setTabPosition(QTabWidget.West)
+        self.tabWidget.setTabPosition(QTabWidget.TabPosition.West)
         self.tabWidget.setDocumentMode(False)
         self.bookmarkTab = QWidget()
         self.bookmarkTab.setObjectName(u"bookmarkTab")
@@ -167,7 +167,7 @@ class Ui_MainWindow(object):
         MainWindow.setCentralWidget(self.centralWidget)
         self.menuBar = QMenuBar(MainWindow)
         self.menuBar.setObjectName(u"menuBar")
-        self.menuBar.setGeometry(QRect(0, 0, 700, 23))
+        self.menuBar.setGeometry(QRect(0, 0, 700, 26))
         self.menuFile = QMenu(self.menuBar)
         self.menuFile.setObjectName(u"menuFile")
         self.menuHelp = QMenu(self.menuBar)
index 6f9f972e0ee3aa0cc1754e7ae5e7d3e91d9eac0c..3fa64cd34afbd0e747698af7f9fdbb179e272d55 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtPdfWidgets import QPdfView
 from PySide6.QtWidgets import QComboBox
index 5240a9de059559580d6372fe0d1c3ecd105cfa18..cccbd2b0de45911d0554ab872a03c34dde05b9c7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from pathlib import Path
@@ -15,7 +16,9 @@ if __name__ == "__main__":
     engine = QQmlApplicationEngine()
     qml_file = Path(__file__).parent / "main.qml"
     engine.load(QUrl.fromLocalFile(qml_file))
-
     if not engine.rootObjects():
         sys.exit(-1)
-    sys.exit(app.exec())
+
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index 02a1e5717d7b7786c38b46ffd0fee4d5fef21d30..c89aea7a44b5b5e99ee35411648b415fc555e9a3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, Qt, Slot
@@ -15,7 +16,7 @@ QML_IMPORT_MAJOR_VERSION = 1
 @QmlElement
 class BaseModel(QAbstractListModel):
 
-    RatioRole = Qt.UserRole + 1
+    RatioRole = Qt.ItemDataRole.UserRole + 1
 
     def __init__(self, parent=None):
         super().__init__(parent=parent)
@@ -27,7 +28,7 @@ class BaseModel(QAbstractListModel):
     def roleNames(self):
         default = super().roleNames()
         default[self.RatioRole] = QByteArray(b"ratio")
-        default[Qt.BackgroundRole] = QByteArray(b"backgroundColor")
+        default[Qt.ItemDataRole.BackgroundRole] = QByteArray(b"backgroundColor")
         return default
 
     def data(self, index, role: int):
@@ -35,9 +36,9 @@ class BaseModel(QAbstractListModel):
             ret = None
         elif not index.isValid():
             ret = None
-        elif role == Qt.DisplayRole:
+        elif role == Qt.ItemDataRole.DisplayRole:
             ret = self.db[index.row()]["text"]
-        elif role == Qt.BackgroundRole:
+        elif role == Qt.ItemDataRole.BackgroundRole:
             ret = self.db[index.row()]["bgColor"]
         elif role == self.RatioRole:
             ret = self.db[index.row()]["ratio"]
@@ -48,7 +49,7 @@ class BaseModel(QAbstractListModel):
     def setData(self, index, value, role):
         if not index.isValid():
             return False
-        if role == Qt.EditRole:
+        if role == Qt.ItemDataRole.EditRole:
             self.db[index.row()]["text"] = value
         return True
 
diff --git a/examples/qml/signals/pytoqml1/main.py b/examples/qml/signals/pytoqml1/main.py
deleted file mode 100644 (file)
index 5bc27a5..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import os
-from pathlib import Path
-import sys
-from PySide6.QtCore import QTimer, QUrl
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQuick import QQuickView
-
-if __name__ == '__main__':
-    app = QGuiApplication(sys.argv)
-
-    timer = QTimer()
-    timer.start(2000)
-
-    view = QQuickView()
-    qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
-    view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
-        sys.exit(-1)
-    root = view.rootObject()
-
-    timer.timeout.connect(root.updateRotater)
-
-    view.show()
-    res = app.exec()
-    # Deleting the view before it goes out of scope is required to make sure all child QML instances
-    # are destroyed in the correct order.
-    del view
-    sys.exit(res)
diff --git a/examples/qml/signals/pytoqml1/pytoqml1.pyproject b/examples/qml/signals/pytoqml1/pytoqml1.pyproject
deleted file mode 100644 (file)
index e6f087c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "files": ["main.py", "view.qml"]
-}
diff --git a/examples/qml/signals/pytoqml1/view.qml b/examples/qml/signals/pytoqml1/view.qml
deleted file mode 100644 (file)
index af2d966..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
-    id: page
-
-    function updateRotater() {
-        rotater.angle = rotater.angle + 45
-    }
-
-    width: 500; height: 200
-    color: "lightgray"
-
-    Rectangle {
-        id: rotater
-        property real angle : 0
-        x: 240
-        width: 100; height: 10
-        color: "black"
-        y: 95
-
-        transform: Rotation {
-            origin.x: 10; origin.y: 5
-            angle: rotater.angle
-            Behavior on angle {
-                SpringAnimation {
-                    spring: 1.4
-                    damping: .05
-                }
-            }
-        }
-    }
-
-}
diff --git a/examples/qml/signals/pytoqml2/main.py b/examples/qml/signals/pytoqml2/main.py
deleted file mode 100644 (file)
index 3a3650a..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import os
-from pathlib import Path
-import sys
-from PySide6.QtCore import QObject, QTimer, QUrl, Signal, Slot
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQuick import QQuickView
-from PySide6.QtQml import QmlElement
-
-
-# To be used on the @QmlElement decorator
-# (QML_IMPORT_MINOR_VERSION is optional)
-QML_IMPORT_NAME = "examples.signals.pytoqml2"
-QML_IMPORT_MAJOR_VERSION = 1
-
-
-@QmlElement
-class RotateValue(QObject):
-    valueChanged = Signal(int, arguments=['val'])
-
-    def __init__(self):
-        super().__init__()
-        self.r = 0
-
-    @Slot()
-    def increment(self):
-        self.r = self.r + 10
-        self.valueChanged.emit(self.r)
-
-
-if __name__ == '__main__':
-    app = QGuiApplication(sys.argv)
-    view = QQuickView()
-
-    rotatevalue = RotateValue()
-    timer = QTimer()
-    timer.start(2000)
-    view.setInitialProperties({"rotatevalue": rotatevalue})
-
-    qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
-    view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
-        sys.exit(-1)
-
-    timer.timeout.connect(rotatevalue.increment)
-
-    view.show()
-    res = app.exec()
-    # Deleting the view before it goes out of scope is required to make
-    # sure all child QML instances are destroyed in the correct order.
-    del view
-    sys.exit(res)
diff --git a/examples/qml/signals/pytoqml2/pytoqml2.pyproject b/examples/qml/signals/pytoqml2/pytoqml2.pyproject
deleted file mode 100644 (file)
index e6f087c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "files": ["main.py", "view.qml"]
-}
diff --git a/examples/qml/signals/pytoqml2/view.qml b/examples/qml/signals/pytoqml2/view.qml
deleted file mode 100644 (file)
index 2e9128f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQml
-
-import examples.signals.pytoqml2 1.0
-
-Rectangle {
-    id: page
-
-    width: 500; height: 200
-    color: "lightgray"
-    required property RotateValue rotatevalue
-
-    Text {
-        id: helloText
-        text: "Hello world!"
-        anchors.horizontalCenter: page.horizontalCenter
-        y: 30
-        font.pointSize: 24; font.bold: true
-    }
-
-    Connections {
-        target: rotatevalue
-        function onValueChanged(val) {
-            helloText.rotation = val
-        }
-    }
-}
diff --git a/examples/qml/signals/qmltopy1/doc/qmltopy1.rst b/examples/qml/signals/qmltopy1/doc/qmltopy1.rst
new file mode 100644 (file)
index 0000000..f5d8ece
--- /dev/null
@@ -0,0 +1,13 @@
+Calling Python Methods from QML
+===============================
+
+Introduce how to invoke Python methods (slots) from QML.
+
+**Key Features:**
+
+- **Python Class with Slots:** Defines a Console class in Python with multiple slots using the
+  :deco:`~PySide6.QtCore.Slot` decorator.
+- **Exposing Python Class to QML:** Uses :deco:`~PySide6.QtQml.QmlElement` to make the Console class
+  available in QML.
+- **Calling Slots from QML:** In QML, instantiates Console and calls its methods in response to user
+  interactions.
index 83966903c7c300007a614fc70bb92fdaf391206c..0644e4c9b486b12c0f01271754ece90ef07ac56c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from pathlib import Path
@@ -40,7 +41,7 @@ if __name__ == '__main__':
 
     qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
     res = app.exec()
diff --git a/examples/qml/signals/qmltopy2/doc/qmltopy2.rst b/examples/qml/signals/qmltopy2/doc/qmltopy2.rst
new file mode 100644 (file)
index 0000000..3a2518d
--- /dev/null
@@ -0,0 +1,12 @@
+Receiving return values from Python in QML
+==========================================
+
+Demonstrate how to call Python methods from QML that return values.
+
+**Key Features:**
+
+- **Python Class with Returning Slot:** Defines a `RotateValue` class with a slot that returns an
+  integer.
+- **Exposing Class to QML:** Uses :deco:`~PySide6.QtQml.QmlElement` to expose RotateValue to QML.
+- **Using Return Values in QML:** Calls the Python method from QML and uses the returned value to
+  update the UI.
index bc2e8d3ed0e66422bf3d92f8541458e1054b349e..aeb8b3b7f52b848943de4f299e7f37ff97f37960 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from pathlib import Path
@@ -35,7 +36,7 @@ if __name__ == '__main__':
 
     qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
     res = app.exec()
diff --git a/examples/qml/signals/qmltopy3/doc/qmltopy3.rst b/examples/qml/signals/qmltopy3/doc/qmltopy3.rst
new file mode 100644 (file)
index 0000000..0c94b4b
--- /dev/null
@@ -0,0 +1,10 @@
+Handling QML Signals in Python
+==============================
+
+Show how to respond to signals emitted from QML in Python.
+
+**Key Features:**
+
+- **Defining Signals in QML:** Defines custom signals like `buttonClicked` and `textRotationChanged`
+- **Connecting Signals to Python Functions:** In Python, connects these signals to functions.
+- **Handling Signals in Python:** Implements the `sayThis` function to handle the signals.
index 6a55548426d4e7791082b51661f156d127b3653f..7656154d852997614d52cf852584ddbdf0bc1a21 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from pathlib import Path
@@ -18,7 +19,7 @@ if __name__ == '__main__':
     view = QQuickView()
     qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
 
     root = view.rootObject()
diff --git a/examples/qml/signals/qmltopy4/doc/qmltopy4.rst b/examples/qml/signals/qmltopy4/doc/qmltopy4.rst
new file mode 100644 (file)
index 0000000..70f0082
--- /dev/null
@@ -0,0 +1,11 @@
+Directly Connecting QML Component Signals to Python Functions
+=============================================================
+
+Teach how to connect signals of specific QML components to Python functions using object names.
+
+**Key Features:**
+
+- **Assigning `objectName` in QML:** Sets `objectName` properties to identify QML objects.
+- **Finding QML Objects in Python:** Uses `findChild` to get references to QML objects.
+- **Connecting Component Signals to Python Functions:** Connects signals directly to Python
+  functions.
index 8a56073d1945d9d350cb338fc2206357d3afefab..a6bd1e0697119a9501e588daba59c100686a8bc6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from pathlib import Path
@@ -18,7 +19,7 @@ if __name__ == '__main__':
     view = QQuickView()
     qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
 
     root = view.rootObject()
index 4e6afc9ffb1b0821b1fd8dacc6a42178dd7bba69..802e9f94b069a2f9e73b9b9af99aac901c128154 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from pathlib import Path
@@ -73,4 +74,6 @@ if __name__ == '__main__':
     if not engine.rootObjects():
         sys.exit(-1)
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index 4c1b3bdaeb580b11f8843c404cd6d0e7e9c46603..a18acb4b71942c61fecb1f89471ebf50ff7df5c9 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-adding-types-example:
-
 Extending QML - Adding Types Example
 ====================================
 
index ec703dbf3c2a620c90d44cbbd755d3f663f56abf..4c556c631e0ca51a7f3ac89b3591c77e5296d49c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/examples/qml/referenceexamples/adding example from Qt v6.x"""
 
index 526eae7142a233ba62c993a14c3bd353b7ecb31c..90e82082a40b946d75fa09dab43edc5fc18e3be0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property
 from PySide6.QtQml import QmlElement
index 764815175d441e15977abf7dfd3e26c014d1230c..875e98a27c9cb43d623daee80f11ceda709e99b4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlElement, ListProperty
index 90a73b78d5f5063faaaaca6728fd77a4f4a7df99..878b122c71814ba981084a245b2cf60661bf14d5 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-advanced1-base-project:
-
 Extending QML (advanced) - BirthdayParty Base Project
 =====================================================
 
index 560db6602db57c15103544e29b38015837c0acad..eccb9ff8ca69fe864410f184c29aac4a4b7c0469 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the
    qml/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project example from Qt v6.x"""
index 60dc9d882c8c0bb78393ebfc79cbf62133992af2..89a669251ba6e5fc86d20cc0e512fde17ad3bba9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlElement
index 764815175d441e15977abf7dfd3e26c014d1230c..875e98a27c9cb43d623daee80f11ceda709e99b4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlElement, ListProperty
index 16c0dbc9ab611db72a20cf4b461198021c68f8ce..e55705d597cfc1cefb65fae29d1432672ef6a524 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-advanced2-inheritance-and-coercion:
-
 Extending QML (advanced) - Inheritance and Coercion
 ===================================================
 
@@ -25,7 +23,7 @@ To do this, the ``Boy`` and ``Girl`` classes are introduced, both inheriting fro
 
 The ``Person`` class remains unaltered and the ``Boy`` and ``Girl`` classes are
 trivial extensions of it. The types and their QML name are registered with the
-QML engine with ``@QmlElement``.
+QML engine with :deco:`~PySide6.QtQml.QmlElement`.
 
 Notice that the ``host`` and ``guests`` properties in ``BirthdayParty`` still
 take instances of ``Person``.
@@ -49,8 +47,9 @@ explicit ``Boy`` or ``Girl`` should be instantiated instead.
 
 While we want to disallow instantiating ``Person`` from within QML, it still
 needs to be registered with the QML engine so that it can be used as a property
-type and other types can be coerced to it. This is what the ``@QmlUncreatable``
-macro does. As all three types, ``Person``, ``Boy`` and ``Girl``, have been
+type and other types can be coerced to it. This is what
+:deco:`~PySide6.QtQml.QmlUncreatable` does.
+As all three types, ``Person``, ``Boy`` and ``Girl``, have been
 registered with the QML system, on assignment, QML automatically (and
 type-safely) converts the ``Boy`` and ``Girl`` objects into a ``Person``.
 
index cc77e2b406fdfd913609395406ae76489621f682..ba9063e2ceeda566afd86590f175f1d6f3b8fb26 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the
    qml/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion example
index 57e73e6f55059fcc435a08c380fe8c69b8c5961c..3d7e0271a522f29ebf0429215a167db9d2a168de 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlElement, QmlUncreatable
index 3f6102c66c58b7d5e98b6386a0cdd7fea9cc79f0..1ef5ae511dd6c8de9b247c94aa1065b610b346d4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, ClassInfo, Property, Signal
 from PySide6.QtQml import QmlElement, ListProperty
index 020974c9b3f6a5cb66e09b271c212648ea66d05c..977a44c4c682a432f327f19cefc0ceda8bee7340 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the
    qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example
index 503aaf65e7699918a93947ded48424e60a7d113f..8deb7d0bfbe1a385ed1e82c8f2ed60341abfb137 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlAnonymous, QmlElement
index 3f6102c66c58b7d5e98b6386a0cdd7fea9cc79f0..1ef5ae511dd6c8de9b247c94aa1065b610b346d4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, ClassInfo, Property, Signal
 from PySide6.QtQml import QmlElement, ListProperty
index 9757b6daa815753077b01550c4c01e43336b0b5c..f15620549ee92881d977b17932a4580ac9e2b6e8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the
    qml/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties example
index ccd439e88d986150801832eb7416f3ef2a7ad120..db3b8d5bd01765b43c6634517b306843e01ef60a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtGui import QColor
index f38bfd3052830f3d09616ad13636aae57cc2794a..8e6628f7cccd435ded15810ebe6f3c076b3c758f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QDate, QObject, ClassInfo, Property, Signal
 from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
index 9a92afeb576cd1e2e0a47db327ecf6e8fef7dda0..c914fe7d2284626752c185f55b8cfca6a6d128d4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the
    qml/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties example
index 503aaf65e7699918a93947ded48424e60a7d113f..8deb7d0bfbe1a385ed1e82c8f2ed60341abfb137 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlAnonymous, QmlElement
index eacb5201dfc870a42008f2a5bea6e01d4b40412f..e946242fd1a3434d3915c700f36a592d24111a41 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QDate, QObject, ClassInfo, Property, QTime, Signal
 from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
index 4e1dc393abf1c9f3d652e527115b9cc0e1d8e563..17e13d49f3e5b8ab298ccfc7be47b1d5004de0a0 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-advanced/advanced6-property-value-source:
-
 Extending QML (advanced) - Property Value Source
 ================================================
 
@@ -16,7 +14,7 @@ the song over time.
     :lines: 13-49
 
 The class ``HappyBirthdaySong`` is added as a value source. It must inherit
-from ``QQmlPropertyValueSource`` and implement its interface. The
+from :class:`~PySide6.QtQml.QQmlPropertyValueSource` and implement its interface. The
 ``setTarget()`` function is used to define which property this source acts
 upon. In this case, the value source writes to the ``announcement`` property of
 the ``BirthdayParty`` to display the lyrics over time. It has an internal timer
index c35f9bffa0a98d70af121e8f62914f8bb73e377c..b76d06ad611d03ea9975c41bf730e219db4df71e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QTimer, Property, Signal, Slot
 from PySide6.QtQml import QmlElement, QPyQmlPropertyValueSource
index ea412a547bbbb066c867eeef0a2a272a640e2057..eb693134a96e3bce77b06a608995667bb21b2970 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the
    qml/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source example
index 503aaf65e7699918a93947ded48424e60a7d113f..8deb7d0bfbe1a385ed1e82c8f2ed60341abfb137 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlAnonymous, QmlElement
index a337d4a16f154e0ed13aa4cd4fd32387ecb51fe6..958a35236e007bc723e6148d6579212a8a8b087e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QDate, QObject, ClassInfo, Property, QTime, Signal
 from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
index 5c0ed21be01756cf64e63e9b5a752ca5b11fac96..8ffffa79f7e208d57a14f35c02e86c91636b46cb 100644 (file)
@@ -1,14 +1,12 @@
-.. _qml-binding-example:
-
 Extending QML - Binding Example
 ===============================
 
-This example builds on the :ref:`qml-adding-types-example`,
-the :ref:`qml-attached-properties-example`,
-the :ref:`qml-default-property-example`,
-the :ref:`qml-inheritance-and-coercion-example`
-the :ref:`qml-object-and-list-property-types-example`
-and the :ref:`qml-valuesource-example`.
+This example builds on the :ref:`example_qml_tutorials_extending-qml-advanced_adding`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced5-attached-properties`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced3-default-properties`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced2-inheritance-and-coercion`
+the :ref:`example_qml_tutorials_extending-qml-advanced_properties`
+and the :ref:`example_qml_tutorials_extending-qml-advanced_advanced6-property-value-source`.
 
 Running the Example
 -------------------
index 59ebfe4c64786450b3e9120ae771e6eb5d39d893..31d477e89f16696e26c9f9baf41c700a841a7c5d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QTimer, Property, Slot
 from PySide6.QtQml import QmlElement, QPyQmlPropertyValueSource
index 64929a80759f42eed09f62e5040debe6ca4ad60a..45d0fd3f91f606f89112ed4bccf13d1158c28c39 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/examples/qml/referenceexamples/binding example from Qt v6.x"""
 
index a6942763ac5e11ad93a303c54be3522e990ae6a1..69ae48c8852ea21e4dcb27f609f951351089d60f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Signal
 from PySide6.QtQml import QmlAnonymous, QmlElement
index 745960535f705ecb0775c505c97c9132f604fc14..f901de9a25b899757821f1dfaa336bc71806ab5f 100644 (file)
@@ -1,16 +1,14 @@
-.. _qml-extension-objects-example:
-
 Extending QML - Extension Objects Example
 =========================================
 
-This example builds on the the :ref:`qml-adding-types-example`.
+This example builds on the the :ref:`example_qml_tutorials_extending-qml-advanced_adding`.
 
-Shows how to use QmlExtended decorator to provide an extension object to a
+Shows how to use :deco:`~PySide6.QtQml.QmlExtended` to provide an extension object to a
 QLineEdit without modifying or subclassing it.
 
 Firstly, the LineEditExtension class is registered with the QML system as an
-extension of QLineEdit. We declare a foreign type to do this as we cannot
-modify Qt's internal QLineEdit class.
+extension of :class:`~PySide6.QtWidgets.QLineEdit`. We declare a foreign type to do
+this as we cannot modify Qt's internal QLineEdit class.
 
 .. code-block:: python
 
@@ -20,7 +18,8 @@ modify Qt's internal QLineEdit class.
     class LineEditForeign(QObject):
 
 
-Note the usage of ``QmlNamedElement()`` instead of ``QmlElement()``.
+Note the usage of :deco:`~PySide6.QtQml.QmlNamedElement` instead of
+:deco:`~PySide6.QtQml.QmlElement`.
 ``QmlElement()`` uses the name of the containing type by default,
 ``LineEditExtension`` in this case. As the class being an extension class is
 an implementation detail, we choose the more natural name ``QLineEdit``
index 6ee386401a2f54f2fddb928aec65d44ca8b46d35..694a042f522247d55e6193f135478f55d850dc7a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/examples/qml/referenceexamples/extended example from Qt v6.x"""
 
index a3942b6714c29bc626258cd6f32b48d9cd50e324..a0034767126cc575a45ccc7bdb5eb61880cb8491 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property, Slot
 from PySide6.QtQml import QmlElement, ListProperty
index bda2ede5a6fe3582dff8c25ebaee7bf0144a3d72..fe7469bd6d698ee2a0ed2691e5ba32246b59071a 100644 (file)
@@ -1,14 +1,12 @@
-.. _qml-methods-example:
-
 Extending QML - Methods Example
 ===============================
 
-This example builds on the :ref:`qml-adding-types-example`,
-the :ref:`qml-object-and-list-property-types-example` and
-the :ref:`qml-inheritance-and-coercion-example`.
+This example builds on the :ref:`example_qml_tutorials_extending-qml-advanced_adding`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_properties` and
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced2-inheritance-and-coercion`.
 
 The Methods Example has an additional method in the ``BirthdayParty`` class:
-``invite()``. ``invite()`` is decorated with ``@Slot`` so that it can be
+``invite()``. ``invite()`` is decorated with :deco:`~PySide6.QtCore.Slot` so that it can be
 called from QML.
 
 In ``example.qml``, the ``invite()`` method is called
index fb656f2667d8a74cca29b7a3ffba5e79eacaef43..e23476b4ddfefe05d4440d554d5c764fa8b5e8ed 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/examples/qml/referenceexamples/methods example from Qt v6.x"""
 
index 526eae7142a233ba62c993a14c3bd353b7ecb31c..90e82082a40b946d75fa09dab43edc5fc18e3be0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property
 from PySide6.QtQml import QmlElement
index 47dddc85d7de4560c22fbe914148cd6a2cb5ef23..be7a2143e5e8f66757440bbaaa31fa09e9046642 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property
 from PySide6.QtQml import QmlElement, ListProperty
index 16924cdcd064c160c040b1f24c1f465922727b52..6655187e2beff2aaf10d0876b0b0d82c503b7767 100644 (file)
@@ -1,11 +1,9 @@
-.. _qml-object-and-list-property-types-example:
-
 Extending QML - Object and List Property Types Example
 ======================================================
 
 Exporting C++ Properties.
 
-This example builds on :ref:`qml-adding-types-example`.
+This example builds on :ref:`example_qml_tutorials_extending-qml-advanced_adding`.
 
 The Object and List Property Types example shows how to add object and list
 properties in QML. This example adds a BirthdayParty type that specifies a
index 11757d5f3a40f8b8ea13d62bd47d282116151a10..f481be165902da3fb8d8a7a670a09c3042cc97c8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/examples/qml/referenceexamples/properties example from Qt v6.x"""
 
index 526eae7142a233ba62c993a14c3bd353b7ecb31c..90e82082a40b946d75fa09dab43edc5fc18e3be0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Property
 from PySide6.QtQml import QmlElement
index 47d0a0e0c2ba67fc6f3acba8f80780253a05e578..322bef954c6e25dcd9dcce3c37899c52c93c8536 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/tutorials/extending-qml/chapter1-basics example from Qt v5.x"""
 
@@ -31,7 +32,7 @@ class PieChart (QQuickPaintedItem):
     def paint(self, painter):
         pen = QPen(self.color, 2)
         painter.setPen(pen)
-        painter.setRenderHints(QPainter.Antialiasing, True)
+        painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
         painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
 
     @Property(QColor, final=True)
@@ -55,10 +56,10 @@ if __name__ == '__main__':
     app = QGuiApplication(sys.argv)
 
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
     res = app.exec()
index 38233aae2b32b65cb0084f8285dae6a67f5673fb..d9bc18cc26f41b8c15a604ce5d6de529117dd2d8 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-chapter1-basics:
-
 Extending QML - Creating a New Type
 ===================================
 
@@ -48,8 +46,8 @@ To do this, we need a C++ class that encapsulates this ``PieChart`` type and
 its two properties. Since QML makes extensive use of Qt's Meta-Object System
 this new class must:
 
-* Inherit from ``QObject``
-* Declare its properties using the ``Property`` decorator
+* Inherit from :class:`~PySide6.QtCore.QObject`
+* Declare its properties using the :class:`~PySide6.QtCore.Property` decorator
 
 Class Implementation
 --------------------
@@ -60,21 +58,22 @@ Here is our ``PieChart`` class, defined in ``basics.py``:
     :lineno-start: 21
     :lines: 21-51
 
-The class inherits from ``QQuickPaintedItem`` because we want to override
-``QQuickPaintedItem.paint()`` to perform drawing operations with the
-``QPainter`` API. If the class just represented some data type and was not an
+The class inherits from :class:`~PySide6.QtQuick.QQuickPaintedItem`
+because we want to override :meth:`~PySide6.QtQuick.QQuickPaintedItem.paint`
+to perform drawing operations with the :class:`~PySide6.QtGui.QPainter` API.
+If the class just represented some data type and was not an
 item that actually needed to be displayed, it could simply inherit from
-``QObject``. Or, if we want to extend the functionality of an existing
+:class:`~PySide6.QtCore.QObject`. Or, if we want to extend the functionality of an existing
 ``QObject``-based class, it could inherit from that class instead.
 Alternatively, if we want to create a visual item that doesn't need to perform
 drawing operations with the ``QPainter`` API, we can just subclass
-``QQuickItem``.
+:class:`~PySide6.QtQuick.QQuickItem`.
 
 The ``PieChart`` class defines the two properties, ``name`` and ``color``, with
 the ``Property`` decorator, and overrides ``QQuickPaintedItem.paint()``. The
-``PieChart`` class is registered using the ``QmlElement`` decorator, to allow
-it to be used from QML. If you don't register the class, ``app.qml`` won't be
-able to create a ``PieChart``.
+``PieChart`` class is registered using the :deco:`~PySide6.QtQml.QmlElement`
+decorator, to allow it to be used from QML. If you don't register the class, ``app.qml``
+won't be able to create a ``PieChart``.
 
 QML Usage
 ---------
@@ -88,13 +87,13 @@ using a standard QML ``Text`` item:
     :lines: 7-26
 
 Notice that although the color is specified as a string in QML, it is
-automatically converted to a ``QColor`` object for the PieChart ``color``
-property. Automatic conversions are provided for various other QML value types.
+automatically converted to a :class:`~PySide6.QtGui.QColor` object for the PieChart
+``color`` property. Automatic conversions are provided for various other QML value types.
 For example, a string like "640x480" can be automatically converted to a
 ``QSize`` value.
 
-We'll also create a main function that uses a ``QQuickView`` to run and display
-``app.qml``. Here is the application ``basics.py``:
+We'll also create a main function that uses a :class:`~PySide6.QtQuick.QQuickView`
+to run and display ``app.qml``. Here is the application ``basics.py``:
 
 .. literalinclude:: basics.py
     :lineno-start: 54
index 245d0ddb2daa17802e7ebcfe43f68072672f3167..c192fb9b64010b1c0d1b0ee582c3f91462d59469 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-chapter2-methods:
-
 Extending QML - Connecting to C++ Methods and Signals
 =====================================================
 
@@ -21,7 +19,7 @@ to our C++ class:
     :lineno-start: 54
     :lines: 54-58
 
-The use of the ``Slot`` decorator makes the ``clearChart()`` method available
+The use of :deco:`~PySide6.QtCore.Slot` makes the ``clearChart()`` method available
 to the Qt Meta-Object system, and in turn, to QML. The method simply changes
 the color to ``Qt::transparent``, repaints the chart, then emits the
 ``chartCleared()`` signal:
index d455c317bb9a5e47d771cdce99ca35ba2c6ecde1..238225fd3e3579c3827bf227b7ef51596e3c6c68 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/tutorials/extending-qml/chapter2-methods example from Qt v5.x"""
 
@@ -32,7 +33,7 @@ class PieChart(QQuickPaintedItem):
     def paint(self, painter):
         pen = QPen(self.color, 2)
         painter.setPen(pen)
-        painter.setRenderHints(QPainter.Antialiasing, True)
+        painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
         painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
 
     @Property(QColor, final=True)
@@ -62,10 +63,10 @@ if __name__ == '__main__':
     app = QGuiApplication(sys.argv)
 
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
     res = app.exec()
index a9b61e7f1268b8aa80a99ceca965b289f4101d6d..ed332cba7524eba1ecb65ebb5d91918ca41c4863 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/tutorials/extending-qml/chapter3-bindings example from Qt v5.x"""
 
@@ -33,7 +34,7 @@ class PieChart (QQuickPaintedItem):
     def paint(self, painter):
         pen = QPen(self._color, 2)
         painter.setPen(pen)
-        painter.setRenderHints(QPainter.Antialiasing, True)
+        painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
         painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
 
     @Property(QColor, notify=colorChanged, final=True)
@@ -66,10 +67,10 @@ if __name__ == '__main__':
     app = QGuiApplication(sys.argv)
 
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
     res = app.exec()
index 3b71911917004b1eb6853f1173a8f136b7bd98b3..8d29c07ad6a30505351aa3bcb5ef583b48142ffb 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-chapter3-bindings:
-
 Extending QML - Adding Property Bindings
 ========================================
 
@@ -24,8 +22,8 @@ clicked, the ``onClicked`` handler in the ``MouseArea`` changes the color of
 ``chartA`` , thereby changing both charts to the color blue.
 
 It's easy to enable property binding for the ``color`` property. We add a
-``notify`` parameter to its ``Property`` decorator to indicate that a
-``colorChanged`` signal is emitted whenever the value changes.
+``notify`` parameter to its :class:`~PySide6.QtCore.Property` decorator to
+indicate that a ``colorChanged`` signal is emitted whenever the value changes.
 
 .. literalinclude:: bindings.py
     :lineno-start: 39
index 659850f38a48ea042cb72aee3f4b51b1cd4b8e24..bf24ec3c3ab0e2de87c1f8684742e9dd61844c0b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/tutorials/extending-qml/chapter4-customPropertyTypes example
    from Qt v5.x"""
@@ -37,7 +38,7 @@ class PieSlice (QQuickPaintedItem):
     def paint(self, painter):
         pen = QPen(self._color, 2)
         painter.setPen(pen)
-        painter.setRenderHints(QPainter.Antialiasing, True)
+        painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
         painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
 
 
@@ -70,10 +71,10 @@ if __name__ == '__main__':
     app = QGuiApplication(sys.argv)
 
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
     res = app.exec()
index f7c3efb1110ee15bb0ff712ee941102eed15c873..394f8261175188b6755875aae5c62c97ff148728 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-chapter4-custompropertytypes:
-
 Extending QML - Using Custom Property Types
 ===========================================
 
@@ -32,7 +30,8 @@ int-type property to store an identifier for each chart:
     }
 
 Aside from ``int``, we could use various other property types. Many of the Qt
-data types such as ``QColor``, ``QSize`` and ``QRect`` are automatically
+data types such as :class:`~PySide6.QtGui.QColor`, :class:`~PySide6.QtCore.QSize`
+and :class:`~PySide6.QtCore.QRect` are automatically
 supported from QML.
 
 If we want to create a property whose type is not supported by QML by default,
@@ -47,8 +46,8 @@ we assign an ``PieSlice`` value which itself contains a ``color``:
     :lines: 4-22
 
 Like ``PieChart``, this new ``PieSlice`` type inherits from
-``QQuickPaintedItem``, is exposed via the ``QmlElement`` decorator and declares
-its properties with the ``Property`` decorator:
+``QQuickPaintedItem``, is exposed via :deco:`~PySide6.QtQml.QmlElement` and declares
+its properties with the :class:`~PySide6.QtCore.Property` decorator:
 
 .. literalinclude:: customPropertyTypes.py
     :lineno-start: 21
@@ -63,7 +62,7 @@ and associated method signatures:
 
 There is one thing to be aware of when implementing ``setPieSlice()``. The
 ``PieSlice`` is a visual item, so it must be set as a child of the ``PieChart``
-using ``QQuickItem.setParentItem()`` so that the ``PieChart`` knows to paint
+using :meth:`~PySide6.QtQuick.QQuickItem.setParentItem` so that the ``PieChart`` knows to paint
 this child item when its contents are drawn.
 
 As with ``PieChart``, we add the ``Charts`` type namespace, version 1.0:
index a98f18c8115d4101c712ca3e300891892e586474..ece79e9d6dd8ec6eb800928393353e58ce5e3c92 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-chapter5-listproperties:
-
 Extending QML - Using List Property Types
 =========================================
 
index 98952cef18878a61379481e7b84a25586d95a909..67d7482ec932446e293fc9de610a23d667573072 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the qml/tutorials/extending-qml/chapter5-listproperties example from Qt v5.x"""
 
@@ -53,7 +54,7 @@ class PieSlice (QQuickPaintedItem):
     def paint(self, painter):
         pen = QPen(self._color, 2)
         painter.setPen(pen)
-        painter.setRenderHints(QPainter.Antialiasing, True)
+        painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
         painter.drawPie(
             self.boundingRect().adjusted(1, 1, -1, -1), self._fromAngle * 16, self._angleSpan * 16)
 
@@ -84,10 +85,10 @@ if __name__ == '__main__':
     app = QGuiApplication(sys.argv)
 
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
     res = app.exec()
index 3ab8bcc08c85ab4dc2e907a03359401ce2d92084..d406fd75879b90e6c61720153bace963143b7cd7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Property
 from PySide6.QtQml import QmlElement, ListProperty
index 6f82f1f10930207264758b7678e29f4207983ec2..6a4a6973149db543612ae82f54b6b46113bf75d2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Property, QRectF
 from PySide6.QtGui import QColor, QPainter, QPen
index a9d10081217171dc62b73a05c038f99c85bffe2e..2320a8fa1193ead03b1eb4fb0ce409e3e8f1ce95 100644 (file)
@@ -1,5 +1,3 @@
-.. _qml-chapter6-plugins-example:
-
 Extending QML - Plugins Example
 ===============================
 
@@ -9,9 +7,9 @@ about extending QML with Python.
 This example refers to the Python version of using a QML plugin in Python. The
 idea of plugins in Python is non-existent because Python modules are
 dynamically loaded anyway. We use this idea and our QML type registration
-decorators - ``QmlELement``/``QmlNamedElement`` - to register the QML modules as they
-are imported. The ``pyside6-qml`` tool does this for you by simply pointing to the
-``.qml`` file.
+decorators - :deco:`~PySide6.QtQml.QmlElement` / :deco:`~PySide6.QtQml.QmlNamedElement` -
+to register the QML modules as they are imported.
+The :ref:`pyside6-qml` tool does this for you by simply pointing to the ``.qml`` file.
 
 .. image:: plugins.png
    :width: 400
index 008a1b94b372a4b665256e2f783e19fae667a0ab..e73eafef0c8990aa41a6780cfdbaa6f9f9a26b06 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from dataclasses import dataclass
@@ -24,7 +25,7 @@ class Person:
 @QmlElement
 @QmlSingleton
 class PersonModel (QAbstractListModel):
-    MyRole = Qt.UserRole + 1
+    MyRole = Qt.ItemDataRole.UserRole + 1
 
     def __init__(self, data, parent=None):
         super().__init__(parent)
@@ -33,7 +34,7 @@ class PersonModel (QAbstractListModel):
     def roleNames(self):
         roles = {
             PersonModel.MyRole: QByteArray(b'myrole'),
-            Qt.DisplayRole: QByteArray(b'display')
+            Qt.ItemDataRole.DisplayRole: QByteArray(b'display')
         }
         return roles
 
@@ -42,10 +43,10 @@ class PersonModel (QAbstractListModel):
 
     def data(self, index, role):
         d = self._data[index.row()]
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             return d.name
-        if role == Qt.DecorationRole:
-            return Qt.black
+        if role == Qt.ItemDataRole.DecorationRole:
+            return Qt.GlobalColor.black
         if role == PersonModel.MyRole:
             return d.myrole
         return None
@@ -59,11 +60,11 @@ class PersonModel (QAbstractListModel):
 if __name__ == '__main__':
     app = QGuiApplication(sys.argv)
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
 
     qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
 
index 968761e5cc99582107d468b32671009cedc06145..0f0da3e360a9cd71106276a32dfb4a2ab1f5ec90 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from pathlib import Path
 import sys
@@ -49,7 +50,7 @@ if __name__ == '__main__':
                 DataObject("Item 4", "yellow")]
 
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     view.setInitialProperties({"model": dataList})
 
     qml_file = Path(__file__).parent / "view.qml"
index 3982b1ffc4e0d9c19f1406b763a4b2e3a03f18dd..a1614b41c7ea543840b482967226255b7f779226 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from pathlib import Path
 import sys
index cf5a05adad75d2ab236fe7f1242d453c9145e09f..95390b6ee3785bf2a7e9a0739db66b96c15509cc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from argparse import ArgumentParser, RawTextHelpFormatter
 from pathlib import Path
@@ -39,8 +40,8 @@ class TextBalloon(QQuickPaintedItem):
         brush = QBrush(QColor("#007430"))
 
         painter.setBrush(brush)
-        painter.setPen(Qt.NoPen)
-        painter.setRenderHint(QPainter.Antialiasing)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
 
         itemSize = self.size()
 
@@ -72,11 +73,11 @@ if __name__ == "__main__":
         QQmlDebuggingEnabler.enableDebugging(True)
     app = QApplication(sys.argv)
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     qml_file = Path(__file__).parent / "main.qml"
     view.setSource(QUrl.fromLocalFile(qml_file))
 
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
 
index 69e7321f9da31d87df41a3549e27d27f577783ca..862c38d7b965180c121d1d4f0777e7594aa6e92e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import ctypes
 import numpy
@@ -102,9 +103,9 @@ class CubeRenderer():
                      w.height() * w.devicePixelRatio())
 
         self.m_program = QOpenGLShaderProgram()
-        self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.Vertex,
+        self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex,
                                                         VERTEXSHADER_SOURCE)
-        self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.Fragment,
+        self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment,
                                                         FRAGMENTSHADER_SOURCE)
         self.m_program.bindAttributeLocation("vertex", 0)
         self.m_program.bindAttributeLocation("coord", 1)
index ee885ae6d0e82af2c5084a8e75bfd34c13929a69..340d35a9f0cdfc3ebcd553dac0499e93d6d13304 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtGui import QGuiApplication
@@ -11,7 +12,7 @@ from window_singlethreaded import WindowSingleThreaded
 if __name__ == "__main__":
     app = QGuiApplication(sys.argv)
     # only functional when Qt Quick is also using OpenGL
-    QQuickWindow.setGraphicsApi(QSGRendererInterface.OpenGLRhi)
+    QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.OpenGLRhi)
     window = WindowSingleThreaded()
     window.resize(1024, 768)
     window.show()
diff --git a/examples/quick/rendercontrol/rendercontrol_opengl/requirements.txt b/examples/quick/rendercontrol/rendercontrol_opengl/requirements.txt
new file mode 100644 (file)
index 0000000..3727213
--- /dev/null
@@ -0,0 +1 @@
+PyOpenGL
index 6f1e61f943ed7d7c46dc704551fffe38b7509106..a40be7d33ac708bdef8892310d8886e817673ae5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import numpy
 from pathlib import Path
@@ -42,7 +43,7 @@ class WindowSingleThreaded(QWindow):
         self.m_quickReady = False
         self.m_dpr = 0
         self.m_status_conn_id = None
-        self.setSurfaceType(QSurface.OpenGLSurface)
+        self.setSurfaceType(QSurface.SurfaceType.OpenGLSurface)
 
         format = QSurfaceFormat()
         # Qt Quick may need a depth and stencil buffer. Always make sure these
index 0e24877bda08e4a3485964cfbea9477353d13b77..a7ff6a223d2e016f9f295e5653baf6f3c05121bd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from pathlib import Path
@@ -13,14 +14,14 @@ from squircle import Squircle  # noqa: F401
 if __name__ == "__main__":
     app = QGuiApplication(sys.argv)
 
-    QQuickWindow.setGraphicsApi(QSGRendererInterface.OpenGL)
+    QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.OpenGL)
 
     view = QQuickView()
-    view.setResizeMode(QQuickView.SizeRootObjectToView)
+    view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
     qml_file = Path(__file__).parent / "main.qml"
     view.setSource(QUrl.fromLocalFile(qml_file))
 
-    if view.status() == QQuickView.Error:
+    if view.status() == QQuickView.Status.Error:
         sys.exit(-1)
     view.show()
 
diff --git a/examples/quick/scenegraph/openglunderqml/requirements.txt b/examples/quick/scenegraph/openglunderqml/requirements.txt
new file mode 100644 (file)
index 0000000..3727213
--- /dev/null
@@ -0,0 +1 @@
+PyOpenGL
index d2900198b3aaa4274f6ff9cfc451fd96cc58e3a6..a1a154967b6ae305c529942b225ded4a2568f563 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Property, QRunnable, Qt, Signal, Slot
 from PySide6.QtQml import QmlElement
@@ -47,9 +48,9 @@ class Squircle(QQuickItem):
     @Slot(QQuickWindow)
     def handleWindowChanged(self, win):
         if win:
-            win.beforeSynchronizing.connect(self.sync, type=Qt.DirectConnection)
-            win.sceneGraphInvalidated.connect(self.cleanup, type=Qt.DirectConnection)
-            win.setColor(Qt.black)
+            win.beforeSynchronizing.connect(self.sync, type=Qt.ConnectionType.DirectConnection)
+            win.sceneGraphInvalidated.connect(self.cleanup, type=Qt.ConnectionType.DirectConnection)
+            win.setColor(Qt.GlobalColor.black)
             self.sync()
 
     @Slot()
@@ -62,9 +63,9 @@ class Squircle(QQuickItem):
         window = self.window()
         if not self._renderer:
             self._renderer = SquircleRenderer()
-            window.beforeRendering.connect(self._renderer.init, Qt.DirectConnection)
+            window.beforeRendering.connect(self._renderer.init, Qt.ConnectionType.DirectConnection)
             window.beforeRenderPassRecording.connect(
-                self._renderer.paint, Qt.DirectConnection
+                self._renderer.paint, Qt.ConnectionType.DirectConnection
             )
         self._renderer.setViewportSize(window.size() * window.devicePixelRatio())
         self._renderer.setT(self._t)
@@ -72,7 +73,7 @@ class Squircle(QQuickItem):
 
     def releaseResources(self):
         self.window().scheduleRenderJob(
-            CleanupJob(self._renderer), QQuickWindow.BeforeSynchronizingStage
+            CleanupJob(self._renderer), QQuickWindow.RenderStage.BeforeSynchronizingStage
         )
         self._renderer = None
 
index d824f96ab010244969dd8c8e5359a26198a6e149..d992199775a62c9d05b34654b8c7466449a14eff 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from textwrap import dedent
 
@@ -56,11 +57,13 @@ class SquircleRenderer(QOpenGLFunctions):
     def init(self):
         if not self._program:
             rif = self._window.rendererInterface()
-            assert (rif.graphicsApi() == QSGRendererInterface.OpenGL)
+            assert (rif.graphicsApi() == QSGRendererInterface.GraphicsApi.OpenGL)
             self.initializeOpenGLFunctions()
             self._program = QOpenGLShaderProgram()
-            self._program.addCacheableShaderFromSourceCode(QOpenGLShader.Vertex, VERTEX_SHADER)
-            self._program.addCacheableShaderFromSourceCode(QOpenGLShader.Fragment, FRAGMENT_SHADER)
+            self._program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex,
+                                                           VERTEX_SHADER)
+            self._program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment,
+                                                           FRAGMENT_SHADER)
             self._program.bindAttributeLocation("vertices", 0)
             self._program.link()
 
index 60a90406569303db4c2eaa9e7ffc271a18147de9..5c588b9620cb3e22b2f451ea9a9aec9b80929fde 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt Quick customgeometry example from Qt v6.x"""
 
@@ -37,7 +38,7 @@ class BezierCurve(QQuickItem):
 
         self._node = None
         self._geometry = None
-        self.setFlag(QQuickItem.Flags.ItemHasContents, True)
+        self.setFlag(QQuickItem.Flag.ItemHasContents, True)
 
     def p1(self):
         return self._p1
@@ -99,11 +100,11 @@ class BezierCurve(QQuickItem):
 
             self._node = QSGGeometryNode()
             self._node.setGeometry(self._geometry)
-            self._node.setFlag(QSGNode.Flags.OwnsGeometry)
+            self._node.setFlag(QSGNode.Flag.OwnsGeometry)
             self._material = QSGFlatColorMaterial()
             self._material.setColor(QColor(255, 0, 0))
             self._node.setMaterial(self._material)
-            self._node.setFlag(QSGNode.Flags.OwnsMaterial)
+            self._node.setFlag(QSGNode.Flag.OwnsMaterial)
         else:
             self._geometry = self._node.geometry()
             self._geometry.allocate(self._segmentCount)
@@ -123,7 +124,7 @@ class BezierCurve(QQuickItem):
 
         self._geometry.setVertexDataAsPoint2D(vertices)
 
-        self._node.markDirty(QSGNode.DirtyGeometry)
+        self._node.markDirty(QSGNode.DirtyStateBit.DirtyGeometry)
         return self._node
 
     p1 = Property(QPointF, p1, setP1, notify=p1Changed)
index e78d1ffa2127687bb29e8f2d7072d79051206994..84402b609c7db9d0a9f8719be48defbf51ec1fb6 100644 (file)
@@ -42,4 +42,3 @@ ListModel {
         fetchImages("json");
     }
 }
-
index 62ba6a5e9ee4f1d6a3ac166f75108e7e53d68c98..f12fa88d2692e1b212b6446da4419397e70b5902 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from pathlib import Path
index 0360806ae00253a0dc1bf3a0b3a1163a32f2f38a..3b4984222a8af850ffce179a4cb8cff44939c57f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import random
 
@@ -47,5 +48,6 @@ class ExamplePointGeometry(QQuick3DGeometry):
         self.setPrimitiveType(QQuick3DGeometry.PrimitiveType.Points)
 
         self.addAttribute(
-            QQuick3DGeometry.Attribute.PositionSemantic, 0, QQuick3DGeometry.Attribute.F32Type
+            QQuick3DGeometry.Attribute.Semantic.PositionSemantic, 0,
+            QQuick3DGeometry.Attribute.ComponentType.F32Type
         )
index 854430c46e6b18f9559fa9df4d619f19f4e3192b..996a9f85c6c5939db64e0c51c6d07756087a0236 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import numpy as np
 from PySide6.QtCore import Property, Signal
@@ -170,14 +171,15 @@ class ExampleTriangleGeometry(QQuick3DGeometry):
         self.setBounds(QVector3D(-1.0, -1.0, 0.0), QVector3D(+1.0, +1.0, 0.0))
         self.setPrimitiveType(QQuick3DGeometry.PrimitiveType.Triangles)
         self.addAttribute(
-            QQuick3DGeometry.Attribute.PositionSemantic, 0, QQuick3DGeometry.Attribute.F32Type
+            QQuick3DGeometry.Attribute.Semantic.PositionSemantic, 0,
+            QQuick3DGeometry.Attribute.ComponentType.F32Type
         )
 
         if self._hasNormals:
             self.addAttribute(
-                QQuick3DGeometry.Attribute.NormalSemantic,
+                QQuick3DGeometry.Attribute.Semantic.NormalSemantic,
                 3 * FLOAT_SIZE,
-                QQuick3DGeometry.Attribute.F32Type,
+                QQuick3DGeometry.Attribute.ComponentType.F32Type,
             )
 
         if self._hasUV:
index 148330d91451108db98f084b7644c4b853bd5231..bff6b4a9573e8579d3783a2a916e862178fb257d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 import os
@@ -26,4 +27,6 @@ if __name__ == "__main__":
     if not engine.rootObjects():
         sys.exit(-1)
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index 827434038703d4a3f3d4a5d2b02d7ae08511bfd4..bb0c95d341432984e8122d4bd2c950e7c64080ae 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 import sys
@@ -21,4 +22,6 @@ if __name__ == "__main__":
     if not engine.rootObjects():
         sys.exit(-1)
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index a577f7ebd52f6bd8f275c3619feb1b8de7919bed..22df990c4827feeb3f0b5540ff7dabede23294df 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Signal, Property, QSize
 from PySide6.QtGui import QColor
@@ -76,7 +77,7 @@ class GradientTexture(QQuick3DTextureData):
 
     def updateTexture(self):
         self.setSize(QSize(self._width, self._height))
-        self.setFormat(QQuick3DTextureData.RGBA8)
+        self.setFormat(QQuick3DTextureData.Format.RGBA8)
         self.setHasTransparency(False)
         self.setTextureData(self.generate_texture())
 
index a732cc01587e31c2e138c10a6852844b40eb345d..05ff4937899b75bcc5b37766325f74589d6f29e0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlApplicationEngine
@@ -24,7 +25,6 @@ if __name__ == "__main__":
     if not engine.rootObjects():
         sys.exit(-1)
 
-    ex = app.exec()
+    exit_code = app.exec()
     del engine
-
-    sys.exit(ex)
+    sys.exit(exit_code)
index 5d2746c2e92c407ca2e0270d00ee64cbdb2b9479..9f17786ce3ab18ac111e462d1f2ccb3634f9eec6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import bisect
 from dataclasses import dataclass
@@ -18,10 +19,10 @@ class ContactModel(QAbstractListModel):
 
     @QEnum
     class ContactRole(IntEnum):
-        FullNameRole = Qt.DisplayRole
-        AddressRole = Qt.UserRole
-        CityRole = Qt.UserRole + 1
-        NumberRole = Qt.UserRole + 2
+        FullNameRole = Qt.ItemDataRole.DisplayRole
+        AddressRole = Qt.ItemDataRole.UserRole
+        CityRole = Qt.ItemDataRole.UserRole + 1
+        NumberRole = Qt.ItemDataRole.UserRole + 2
 
     @dataclass
     class Contact:
index d501bbdb4646185fc0558d2a6c915b3872216652..c9ce11e51bef88ba43986ea8e6aa751a43d25096 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 PySide6 port of Qt Quick Controls Contact List example from Qt v6.x
@@ -23,6 +24,6 @@ if __name__ == '__main__':
     if not engine.rootObjects():
         sys.exit(-1)
 
-    ex = app.exec()
+    exit_code = app.exec()
     del engine
-    sys.exit(ex)
+    sys.exit(exit_code)
index b46cbec7918440325dfd97f1409781c901d557d9..98034ba49e9adcb8951a37c6d069ccf9cb5841dc 100644 (file)
@@ -1,5 +1,3 @@
-.. _filesystemexplorer_example:
-
 Qt Quick Controls - Filesystem Explorer
 =======================================
 
index 688147726571ae01968bdc65fe174c34e0a023f1..f317fb48a7818f64c0cca0f96fdd23c4677ce8f9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QFileSystemModel
 from PySide6.QtQuick import QQuickTextDocument
index 8fad951cb83c9659df28ae6f3089327d7a297d84..9c63ec38509a87dd4ede642dd15488fb6bf71b7c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
@@ -45,4 +46,6 @@ if __name__ == '__main__':
         fsm = engine.singletonInstance("FileSystemModule", "FileSystemModel")
         fsm.setInitialDirectory(args[0])
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index 6c2a3612ec853dfecf4e4ceebf471ae4834d9d97..d454cf53e4aa6119452d4668bae70b7133dac938 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 The gallery example is a simple application with a drawer menu that contains
@@ -49,4 +50,6 @@ if __name__ == "__main__":
     window = rootObjects[0]
     window.setIcon(QIcon(':/qt-project.org/logos/pysidelogo.png'))
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
diff --git a/examples/remoteobjects/modelview/doc/modelview.rst b/examples/remoteobjects/modelview/doc/modelview.rst
new file mode 100644 (file)
index 0000000..8fc6780
--- /dev/null
@@ -0,0 +1,12 @@
+Model-View Server Example
+=========================
+
+Developing a simple server program that displays and makes changes to a
+QTreeView which is made available on a Remote Objects network. It
+corresponds to the Qt example `Model-View Server Example
+<https://doc.qt.io/qt-6/qtremoteobjects-modelviewserver-example.html>`_.
+
+.. image:: modelview.png
+    :align: center
+    :alt: modelview screenshot
+    :width: 400
index 38409c99875874aad659c3dbda53e1739e73c7fb..c311d1fe1e0333f255c745ff71d9678b011cc08b 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2017 Ford Motor Company
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the remoteobjects/modelviewclient example from Qt v5.x"""
 
index 0e213fb57b6215432990432975621dbfa5b46bea..ec85e5214c109bb15fa01f222a610ace55b466e4 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2017 Ford Motor Company
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the remoteobjects/modelviewserver example from Qt v5.x"""
 
@@ -20,15 +21,17 @@ class TimerHandler(QObject):
     def change_data(self):
         for i in range(10, 50):
             self._model.setData(self._model.index(i, 1),
-                                QColor(Qt.blue), Qt.BackgroundRole)
+                                QColor(Qt.GlobalColor.blue),
+                                Qt.ItemDataRole.BackgroundRole)
 
     def insert_data(self):
         self._model.insertRows(2, 9)
         for i in range(2, 11):
             self._model.setData(self._model.index(i, 1),
-                                QColor(Qt.green), Qt.BackgroundRole)
+                                QColor(Qt.GlobalColor.green),
+                                Qt.ItemDataRole.BackgroundRole)
             self._model.setData(self._model.index(i, 1),
-                                "InsertedRow", Qt.DisplayRole)
+                                "InsertedRow", Qt.ItemDataRole.DisplayRole)
 
     def remove_data(self):
         self._model.removeRows(2, 4)
@@ -37,7 +40,7 @@ class TimerHandler(QObject):
         item = self._model.item(0, 0)
         item.setEnabled(False)
         item = item.child(0, 0)
-        item.setFlags(item.flags() & Qt.ItemIsSelectable)
+        item.setFlags(item.flags() & Qt.ItemFlag.ItemIsSelectable)
 
     def move_data(self):
         self._model.moveRows(QModelIndex(), 2, 4, QModelIndex(), 10)
@@ -69,19 +72,19 @@ if __name__ == '__main__':
             first_item.appendRow(add_child(2, 2))
         second_item = QStandardItem(f"FancyRow2TextNumber {i}")
         if i % 2 == 0:
-            first_item.setBackground(Qt.red)
+            first_item.setBackground(Qt.GlobalColor.red)
         row = [first_item, second_item]
         source_model.invisibleRootItem().appendRow(row)
         data_list.append(f"FancyTextNumber {i}")
 
     # Needed by QMLModelViewClient
     role_names = {
-        Qt.DisplayRole: QByteArray(b'_text'),
-        Qt.BackgroundRole: QByteArray(b'_color')
+        Qt.ItemDataRole.DisplayRole: QByteArray(b'_text'),
+        Qt.ItemDataRole.BackgroundRole: QByteArray(b'_color')
     }
     source_model.setItemRoleNames(role_names)
 
-    roles = [Qt.DisplayRole, Qt.BackgroundRole]
+    roles = [Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.BackgroundRole]
 
     print("Creating registry host")
     node = QRemoteObjectRegistryHost(QUrl("local:registry"))
index b0786355fcbf00f84871d3a33d24bafb96c8f774..883c74c090309399fbedc9f9097f171a8bcb34f1 100644 (file)
@@ -7,5 +7,6 @@
               "../main.py",
               "../truck.cpp",
               "../truck.h",
-              "../CMakeLists.txt"]
+              "../CMakeLists.txt",
+              "../../utils/pyside_config.py"]
 }
index 51b6b4c20297397fd4f3a0363dd3214381866819..930405c5d2a3192735de0ea56e5c2d8348989003 100644 (file)
@@ -48,7 +48,7 @@ placed in separate ``.cpp`` files named after each C++ type. The code is
 then compiled and linked into a shared library. The shared library is a
 CPython extension module, which is loaded by the Python interpreter.
 
-Beacuse the C++ language has different semantics to Python, shiboken
+Because the C++ language has different semantics to Python, shiboken
 needs help in figuring out how to generate the bindings code. This is
 done by specifying a special XML file called a typesystem file.
 
@@ -186,6 +186,7 @@ Run CMake on Windows:
 To build:
 
 .. code-block:: bash
+
     ninja
     ninja install
     cd ..
index cb9f116ee0cd347304f9fb34e40ef9f528c265f9..125073d296c6230033442589e9d0620e6fb81f05 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """An example showcasing how to use bindings for a custom non-Qt C++ library"""
 
index eee54112570d9e084049450b8012c6ef32cd10de..1b1545c78f1b77f80c6de2664ebb30ff176b44c1 100644 (file)
@@ -1,9 +1,11 @@
 {
     "files": ["../main.cpp",
+              "../scriptableapplication.xml",
               "../mainwindow.cpp",
               "../mainwindow.h",
               "../pythonutils.cpp",
               "../pythonutils.h",
               "../wrappedclasses.h",
-              "../CMakeLists.txt"]
+              "../CMakeLists.txt",
+              "../../utils/pyside_config.py"]
 }
index ece7989e772ad83d859f86a80b0664906ffc3057..5dc6fd7f0961571a6e40aef6b8f5592e005ec675 100644 (file)
@@ -5,6 +5,7 @@
 #include "pythonutils.h"
 
 #include <QtWidgets/QApplication>
+#include <QtWidgets/QLabel>
 #include <QtWidgets/QMenu>
 #include <QtWidgets/QMenuBar>
 #include <QtWidgets/QPlainTextEdit>
@@ -15,6 +16,7 @@
 #include <QtGui/QAction>
 #include <QtGui/QFontDatabase>
 #include <QtGui/QIcon>
+#include <QtGui/QTextCursor>
 
 #include <QtCore/QDebug>
 #include <QtCore/QTextStream>
@@ -69,6 +71,11 @@ MainWindow::MainWindow()
     m_scriptEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
     setCentralWidget(m_scriptEdit);
 
+    m_lineNumberLabel = new QLabel;
+    statusBar()->addPermanentWidget(m_lineNumberLabel, 0);
+    connect(m_scriptEdit, &QPlainTextEdit::cursorPositionChanged,
+            this, &MainWindow::slotCursorChanged);
+
     if (!PythonUtils::bindAppObject("__main__"_L1, "mainWindow"_L1,
                                     PythonUtils::MainWindowType, this)) {
        statusBar()->showMessage(tr("Error loading the application module"));
@@ -105,3 +112,10 @@ void MainWindow::testFunction1()
     qDebug().noquote() << message;
     statusBar()->showMessage(message);
 }
+
+void MainWindow::slotCursorChanged()
+{
+    auto cursor = m_scriptEdit->textCursor();
+    const int line = cursor.blockNumber() + 1;
+    m_lineNumberLabel->setText("Line: "_L1 + QString::number(line));
+}
index e72f5ca72d9e74ca7aa6eede795d918ec9eb4214..98af12eb54aef41483d6e005413e0c7920d0cd0c 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <QtWidgets/QMainWindow>
 
+QT_FORWARD_DECLARE_CLASS(QLabel)
 QT_FORWARD_DECLARE_CLASS(QPlainTextEdit)
 
 class MainWindow : public QMainWindow
@@ -19,6 +20,7 @@ public:
     static constexpr auto TEST = QLatin1StringView("test");
 
 private Q_SLOTS:
+    void slotCursorChanged();
     void slotRunScript();
     void slotPrintDiagnostics();
 
@@ -26,6 +28,7 @@ private:
     void runScript(const QString &);
 
     QPlainTextEdit *m_scriptEdit;
+    QLabel *m_lineNumberLabel;
 };
 
 #endif // MAINWINDOW_H
index 68c70baac5e21061517b49c4ccb18d037d94435a..952967c17cec7b27f34178ed7496d13eff721577 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QComboBox
 from PySide6.QtGui import QIntValidator
index 5e60492658958b05d8043f86b342a2d7eae83fa5..15f3cc2bc00806d8f8a3237bb198e4c45c241dee 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt
 from PySide6.QtWidgets import QGroupBox
@@ -8,8 +9,8 @@ from ui_canbusdeviceinfobox import Ui_CanBusDeviceInfoBox
 
 
 def _set_readonly_and_compact(box):
-    box.setAttribute(Qt.WA_TransparentForMouseEvents)
-    box.setFocusPolicy(Qt.NoFocus)
+    box.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+    box.setFocusPolicy(Qt.FocusPolicy.NoFocus)
     box.setStyleSheet("margin-top:0; margin-bottom:0;")
 
 
index d4dd84fb2a117e1a045e97f2c6c97b1eb1041028..39e2dd88e725bbea671df64d9ab0cfee489e6175 100644 (file)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>319</width>
-    <height>217</height>
+    <height>257</height>
    </rect>
   </property>
   <property name="windowTitle">
index dfb4e6025d63c5b739d8fc4cdca67c1ab2ee0dde..ced446b0a29d53986c45ae987e9f01ca83d52228 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QDialog
 
index 6328a06d1fa5c061c66a600a0ae123701a9174d2..fc3f63534e08bf580b623c6a3442c60fa8f7137a 100644 (file)
@@ -29,7 +29,7 @@
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
-        <enum>Qt::Horizontal</enum>
+        <enum>Qt::Orientation::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
index a6d5ac378d71d094b482884491f4a0c9e66ace11..bd612ba7c8810d0a73b52f0a2305335a2307a11f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QSettings, Qt, Slot
 from PySide6.QtGui import QIntValidator
@@ -50,7 +51,7 @@ class ConnectDialog(QDialog):
         self.m_ui.useConfigurationBox.toggled.connect(self.m_ui.configurationBox.setEnabled)
         self.m_ui.pluginListBox.currentTextChanged.connect(self.plugin_changed)
         self.m_ui.interfaceListBox.currentTextChanged.connect(self.interface_changed)
-        self.m_ui.ringBufferBox.stateChanged.connect(self._ring_buffer_changed)
+        self.m_ui.ringBufferBox.checkStateChanged.connect(self._ring_buffer_changed)
 
         self.m_ui.rawFilterEdit.hide()
         self.m_ui.rawFilterLabel.hide()
@@ -61,7 +62,7 @@ class ConnectDialog(QDialog):
 
     @Slot(int)
     def _ring_buffer_changed(self, state):
-        self.m_ui.ringBufferLimitBox.setEnabled(state == Qt.CheckState.Checked.value)
+        self.m_ui.ringBufferLimitBox.setEnabled(state == Qt.CheckState.Checked)
 
     def settings(self):
         return self.m_currentSettings
@@ -149,7 +150,8 @@ class ConnectDialog(QDialog):
             if k == key:
                 result = v
                 break
-        if not result and (key == QCanBusDevice.LoopbackKey or key == QCanBusDevice.ReceiveOwnKey):
+        if (not result and (key == QCanBusDevice.ConfigurationKey.LoopbackKey
+                            or key == QCanBusDevice.ConfigurationKey.ReceiveOwnKey)):
             return "unspecified"
         return str(result)
 
@@ -162,22 +164,22 @@ class ConnectDialog(QDialog):
         self.m_ui.ringBufferLimitBox.setValue(self.m_currentSettings.model_ring_buffer_size)
         self.m_ui.autoscrollBox.setChecked(self.m_currentSettings.use_autoscroll)
 
-        value = self.configuration_value(QCanBusDevice.LoopbackKey)
+        value = self.configuration_value(QCanBusDevice.ConfigurationKey.LoopbackKey)
         self.m_ui.loopbackBox.setCurrentText(value)
 
-        value = self.configuration_value(QCanBusDevice.ReceiveOwnKey)
+        value = self.configuration_value(QCanBusDevice.ConfigurationKey.ReceiveOwnKey)
         self.m_ui.receiveOwnBox.setCurrentText(value)
 
-        value = self.configuration_value(QCanBusDevice.ErrorFilterKey)
+        value = self.configuration_value(QCanBusDevice.ConfigurationKey.ErrorFilterKey)
         self.m_ui.errorFilterEdit.setText(value)
 
-        value = self.configuration_value(QCanBusDevice.BitRateKey)
+        value = self.configuration_value(QCanBusDevice.ConfigurationKey.BitRateKey)
         self.m_ui.bitrateBox.setCurrentText(value)
 
-        value = self.configuration_value(QCanBusDevice.CanFdKey)
+        value = self.configuration_value(QCanBusDevice.ConfigurationKey.CanFdKey)
         self.m_ui.canFdBox.setCurrentText(value)
 
-        value = self.configuration_value(QCanBusDevice.DataBitRateKey)
+        value = self.configuration_value(QCanBusDevice.ConfigurationKey.DataBitRateKey)
         self.m_ui.dataBitrateBox.setCurrentText(value)
 
     def update_settings(self):
index 97e6e005012f6fb700e18607f65211b259f513db..beda14b3d7cde49f601f84f32b0156cd67c5bb66 100644 (file)
@@ -66,7 +66,7 @@
                <number>10</number>
               </property>
               <property name="stepType">
-               <enum>QAbstractSpinBox::AdaptiveDecimalStepType</enum>
+               <enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
               </property>
               <property name="value">
                <number>1000</number>
         <item row="0" column="1">
          <widget class="QLineEdit" name="rawFilterEdit">
           <property name="alignment">
-           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
           </property>
          </widget>
         </item>
         <item row="1" column="1">
          <widget class="QLineEdit" name="errorFilterEdit">
           <property name="alignment">
-           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
           </property>
           <property name="placeholderText">
            <string>FrameError bits</string>
        <item>
         <spacer name="horizontalSpacer">
          <property name="orientation">
-          <enum>Qt::Horizontal</enum>
+          <enum>Qt::Orientation::Horizontal</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
index 97cdcc908924d80c4e3005f2aa02733872ea07fc..0e4aefd3c42f8ffe1ac6a6306ae1d09daa4f9a7d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index ae2a08f1ff5aedfd2208d022ba3fdb181c9a436e..5800af54fb2134fc1e7ac927891994eb9ba2ece7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QTimer, QUrl, Slot
 from PySide6.QtGui import QDesktopServices
@@ -135,11 +136,11 @@ class MainWindow(QMainWindow):
             self.m_ui.actionDisconnect.setEnabled(True)
             self.m_ui.actionDeviceInformation.setEnabled(True)
             self.m_ui.sendFrameBox.setEnabled(True)
-            config_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.BitRateKey)
-            if config_bit_rate > 0:
-                is_can_fd = bool(self.m_can_device.configurationParameter(QCanBusDevice.CanFdKey))
+            config_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.ConfigurationKey.BitRateKey)  # noqa: E501
+            if config_bit_rate is not None and config_bit_rate > 0:
+                is_can_fd = bool(self.m_can_device.configurationParameter(QCanBusDevice.ConfigurationKey.CanFdKey))  # noqa: E501
                 config_data_bit_rate = self.m_can_device.configurationParameter(
-                    QCanBusDevice.DataBitRateKey)
+                    QCanBusDevice.ConfigurationKey.DataBitRateKey)
                 bit_rate = config_bit_rate / 1000
                 if is_can_fd and config_data_bit_rate > 0:
                     data_bit_rate = config_data_bit_rate / 1000
index 61c7760e0c1b92ca99b622b6220098f04afd2c8b..8c7ef841f048752fc67607a26b6aff5596f74a34 100644 (file)
@@ -33,7 +33,7 @@
          <item>
           <widget class="ReceivedFramesView" name="receivedFramesView">
            <property name="editTriggers">
-            <set>QAbstractItemView::NoEditTriggers</set>
+            <set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
            </property>
            <property name="showDropIndicator" stdset="0">
             <bool>false</bool>
@@ -42,7 +42,7 @@
             <bool>false</bool>
            </property>
            <property name="selectionBehavior">
-            <enum>QAbstractItemView::SelectRows</enum>
+            <enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
            </property>
           </widget>
          </item>
@@ -58,7 +58,7 @@
            <item>
             <spacer name="horizontalSpacer">
              <property name="orientation">
-              <enum>Qt::Horizontal</enum>
+              <enum>Qt::Orientation::Horizontal</enum>
              </property>
              <property name="sizeHint" stdset="0">
               <size>
@@ -83,7 +83,7 @@
      <x>0</x>
      <y>0</y>
      <width>634</width>
-     <height>22</height>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuCalls">
index 7953f3750c2c2419ea96e058b29e90dd94adbd62..d18354e0baeab9bae10aef02edada0e1937dcda1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import IntEnum
 
@@ -16,12 +17,15 @@ class ReceivedFramesModelColumns(IntEnum):
     count = 6
 
 
-clipboard_text_role = Qt.UserRole + 1
+clipboard_text_role = Qt.ItemDataRole.UserRole + 1
 
 
-column_alignment = [Qt.AlignRight | Qt.AlignVCenter, Qt.AlignRight | Qt.AlignVCenter,
-                    Qt.AlignCenter, Qt.AlignRight | Qt.AlignVCenter,
-                    Qt.AlignRight | Qt.AlignVCenter, Qt.AlignLeft | Qt.AlignVCenter]
+column_alignment = [Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+                    Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+                    Qt.AlignmentFlag.AlignCenter,
+                    Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+                    Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+                    Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter]
 
 
 class ReceivedFramesModel(QAbstractTableModel):
@@ -39,7 +43,7 @@ class ReceivedFramesModel(QAbstractTableModel):
         return True
 
     def headerData(self, section, orientation, role):
-        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
+        if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
             if section == ReceivedFramesModelColumns.number:
                 return "#"
             if section == ReceivedFramesModelColumns.timestamp:
@@ -53,7 +57,7 @@ class ReceivedFramesModel(QAbstractTableModel):
             if section == ReceivedFramesModelColumns.data:
                 return "Data"
 
-        if role == Qt.SizeHintRole and orientation == Qt.Horizontal:
+        if role == Qt.ItemDataRole.SizeHintRole and orientation == Qt.Orientation.Horizontal:
             if section == ReceivedFramesModelColumns.number:
                 return QSize(80, 25)
             if section == ReceivedFramesModelColumns.timestamp:
@@ -73,9 +77,9 @@ class ReceivedFramesModel(QAbstractTableModel):
             return None
         row = index.row()
         column = index.column()
-        if role == Qt.TextAlignmentRole:
+        if role == Qt.ItemDataRole.TextAlignmentRole:
             return column_alignment[index.column()]
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.AlignmentFlag.DisplayRole:
             return self.m_framesQueue[row][column]
         if role == clipboard_text_role:
             f = self.m_framesQueue[row][column]
index 0f99a1e77078e67ded8c75fdeda21dc84048a2b5..c4a7f6653177c2b847f66caac25bd839e1e86844 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QPoint, Qt, Slot
 from PySide6.QtGui import QAction, QKeySequence
@@ -12,7 +13,7 @@ class ReceivedFramesView(QTableView):
 
     def __init__(self, parent):
         super().__init__(parent)
-        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
         self.customContextMenuRequested.connect(self._context_menu)
 
     @Slot(QPoint)
@@ -31,7 +32,7 @@ class ReceivedFramesView(QTableView):
     def set_model(self, model):
         super().setModel(model)
         for i in range(0, model.columnCount()):
-            size = model.headerData(i, Qt.Horizontal, Qt.SizeHintRole)
+            size = model.headerData(i, Qt.Orientation.Horizontal, Qt.ItemDataRole.SizeHintRole)
             self.setColumnWidth(i, size.width())
 
     def keyPressEvent(self, event):
index 6472fc473c82caa33d8290d20f6e10aabf0326a7..b151b79ab11059dd15503a4b3e71ed7182857aee 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import re
 
index d913e334ca3aa2de83c9e6d3ac9d521c78fae88a..110c7e735762ab977aab67f78a3fbfd465c6ee52 100644 (file)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>485</width>
-    <height>206</height>
+    <width>505</width>
+    <height>219</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -18,7 +18,7 @@
   </property>
   <layout class="QVBoxLayout" name="verticalLayout_4">
    <property name="sizeConstraint">
-    <enum>QLayout::SetMinimumSize</enum>
+    <enum>QLayout::SizeConstraint::SetMinimumSize</enum>
    </property>
    <item>
     <widget class="QGroupBox" name="frameTypeBox">
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout_3">
      <property name="sizeConstraint">
-      <enum>QLayout::SetMinimumSize</enum>
+      <enum>QLayout::SizeConstraint::SetMinimumSize</enum>
      </property>
      <item>
       <layout class="QVBoxLayout" name="verticalLayout">
index d2f0e74a58303e243ca17e01cde218a3ed61582c..c4d4fc561f47504aee61c23ce309df59f9ec8c22 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'canbusdeviceinfobox.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -22,7 +22,7 @@ class Ui_CanBusDeviceInfoBox(object):
     def setupUi(self, CanBusDeviceInfoBox):
         if not CanBusDeviceInfoBox.objectName():
             CanBusDeviceInfoBox.setObjectName(u"CanBusDeviceInfoBox")
-        CanBusDeviceInfoBox.resize(319, 217)
+        CanBusDeviceInfoBox.resize(319, 257)
         self.verticalLayout = QVBoxLayout(CanBusDeviceInfoBox)
         self.verticalLayout.setObjectName(u"verticalLayout")
         self.pluginLabel = QLabel(CanBusDeviceInfoBox)
index c32e9ca2d62511d7b13840191085507488737d08..808e8ae952ae7bcfcf141ad84b2943c27a491ea4 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'canbusdeviceinfodialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
index f01adc4ebebcd1c88166ea95a12f15f559421247..d69f28765e0318b10f59f7f100518ee677858f9c 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'connectdialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -63,7 +63,7 @@ class Ui_ConnectDialog(object):
         self.ringBufferLimitBox.setMinimum(10)
         self.ringBufferLimitBox.setMaximum(10000000)
         self.ringBufferLimitBox.setSingleStep(10)
-        self.ringBufferLimitBox.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType)
+        self.ringBufferLimitBox.setStepType(QAbstractSpinBox.StepType.AdaptiveDecimalStepType)
         self.ringBufferLimitBox.setValue(1000)
 
         self.horizontalLayout_2.addWidget(self.ringBufferLimitBox)
@@ -99,7 +99,7 @@ class Ui_ConnectDialog(object):
 
         self.rawFilterEdit = QLineEdit(self.configurationBox)
         self.rawFilterEdit.setObjectName(u"rawFilterEdit")
-        self.rawFilterEdit.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.rawFilterEdit.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout_4.addWidget(self.rawFilterEdit, 0, 1, 1, 1)
 
@@ -110,7 +110,7 @@ class Ui_ConnectDialog(object):
 
         self.errorFilterEdit = QLineEdit(self.configurationBox)
         self.errorFilterEdit.setObjectName(u"errorFilterEdit")
-        self.errorFilterEdit.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.errorFilterEdit.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout_4.addWidget(self.errorFilterEdit, 1, 1, 1, 1)
 
index 4ba4dd309483899eda0141973ced22bce05118b4..e08c093a9fde176a8b69c52561beba0e08af1592 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'mainwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -33,24 +33,24 @@ class Ui_MainWindow(object):
         self.actionConnect = QAction(MainWindow)
         self.actionConnect.setObjectName(u"actionConnect")
         icon = QIcon()
-        icon.addFile(u":/images/connect.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon.addFile(u":/images/connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionConnect.setIcon(icon)
         self.actionDisconnect = QAction(MainWindow)
         self.actionDisconnect.setObjectName(u"actionDisconnect")
         icon1 = QIcon()
-        icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionDisconnect.setIcon(icon1)
         self.actionQuit = QAction(MainWindow)
         self.actionQuit.setObjectName(u"actionQuit")
         icon2 = QIcon()
-        icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionQuit.setIcon(icon2)
         self.actionAboutQt = QAction(MainWindow)
         self.actionAboutQt.setObjectName(u"actionAboutQt")
         self.actionClearLog = QAction(MainWindow)
         self.actionClearLog.setObjectName(u"actionClearLog")
         icon3 = QIcon()
-        icon3.addFile(u":/images/clear.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon3.addFile(u":/images/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionClearLog.setIcon(icon3)
         self.actionPluginDocumentation = QAction(MainWindow)
         self.actionPluginDocumentation.setObjectName(u"actionPluginDocumentation")
@@ -80,10 +80,10 @@ class Ui_MainWindow(object):
         self.verticalLayout_2.setObjectName(u"verticalLayout_2")
         self.receivedFramesView = ReceivedFramesView(self.receivedMessagesBox)
         self.receivedFramesView.setObjectName(u"receivedFramesView")
-        self.receivedFramesView.setEditTriggers(QAbstractItemView.NoEditTriggers)
-        self.receivedFramesView.setProperty("showDropIndicator", False)
+        self.receivedFramesView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+        self.receivedFramesView.setProperty(u"showDropIndicator", False)
         self.receivedFramesView.setDragDropOverwriteMode(False)
-        self.receivedFramesView.setSelectionBehavior(QAbstractItemView.SelectRows)
+        self.receivedFramesView.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
 
         self.verticalLayout_2.addWidget(self.receivedFramesView)
 
@@ -111,7 +111,7 @@ class Ui_MainWindow(object):
         MainWindow.setCentralWidget(self.centralWidget)
         self.menuBar = QMenuBar(MainWindow)
         self.menuBar.setObjectName(u"menuBar")
-        self.menuBar.setGeometry(QRect(0, 0, 634, 22))
+        self.menuBar.setGeometry(QRect(0, 0, 634, 26))
         self.menuCalls = QMenu(self.menuBar)
         self.menuCalls.setObjectName(u"menuCalls")
         self.menuHelp = QMenu(self.menuBar)
index 79f013c89ef7f9258a92f4f1be9e0ca74fd50c63..204defec7505f7dcd4267c090ee20e61e047ccfc 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'sendframebox.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -23,10 +23,10 @@ class Ui_SendFrameBox(object):
     def setupUi(self, SendFrameBox):
         if not SendFrameBox.objectName():
             SendFrameBox.setObjectName(u"SendFrameBox")
-        SendFrameBox.resize(485, 206)
+        SendFrameBox.resize(505, 219)
         self.verticalLayout_4 = QVBoxLayout(SendFrameBox)
         self.verticalLayout_4.setObjectName(u"verticalLayout_4")
-        self.verticalLayout_4.setSizeConstraint(QLayout.SetMinimumSize)
+        self.verticalLayout_4.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
         self.frameTypeBox = QGroupBox(SendFrameBox)
         self.frameTypeBox.setObjectName(u"frameTypeBox")
         self.frameTypeBox.setCheckable(False)
@@ -78,7 +78,7 @@ class Ui_SendFrameBox(object):
 
         self.horizontalLayout_3 = QHBoxLayout()
         self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
-        self.horizontalLayout_3.setSizeConstraint(QLayout.SetMinimumSize)
+        self.horizontalLayout_3.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
         self.verticalLayout = QVBoxLayout()
         self.verticalLayout.setObjectName(u"verticalLayout")
         self.frameIdLabel = QLabel(SendFrameBox)
index 1bad75c49c311547b10c394d8589d9f684b1bd21..de3e946b6e3e78189999229b5438fb6908d2e065 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the examples/serialbus/modbus/client example from Qt v6.x"""
 
index 02f9d478b9fba6aa85def24c85d328d8f4fd7d88..97de08d190ae57391794fd7b4ed28406b70e859f 100644 (file)
@@ -1,14 +1,13 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
-import functools
 from enum import IntEnum
 
 from PySide6.QtCore import QUrl, Slot
 from PySide6.QtGui import QStandardItemModel, QStandardItem
 from PySide6.QtWidgets import QMainWindow
-from PySide6.QtSerialBus import (QModbusDataUnit,
-                                 QModbusDevice, QModbusReply,
+from PySide6.QtSerialBus import (QModbusDataUnit, QModbusDevice,
                                  QModbusRtuSerialClient, QModbusTcpClient)
 
 from ui_mainwindow import Ui_MainWindow
@@ -43,10 +42,11 @@ class MainWindow(QMainWindow):
         vp = self.ui.writeValueTable.viewport()
         self._write_model.update_viewport.connect(vp.update)
 
-        self.ui.writeTable.addItem("Coils", QModbusDataUnit.Coils)
-        self.ui.writeTable.addItem("Discrete Inputs", QModbusDataUnit.DiscreteInputs)
-        self.ui.writeTable.addItem("Input Registers", QModbusDataUnit.InputRegisters)
-        self.ui.writeTable.addItem("Holding Registers", QModbusDataUnit.HoldingRegisters)
+        self.ui.writeTable.addItem("Coils", QModbusDataUnit.RegisterType.Coils)
+        self.ui.writeTable.addItem("Discrete Inputs", QModbusDataUnit.RegisterType.DiscreteInputs)
+        self.ui.writeTable.addItem("Input Registers", QModbusDataUnit.RegisterType.InputRegisters)
+        self.ui.writeTable.addItem("Holding Registers",
+                                   QModbusDataUnit.RegisterType.HoldingRegisters)
 
         self.ui.connectType.setCurrentIndex(0)
         self.onConnectTypeChanged(0)
@@ -111,7 +111,7 @@ class MainWindow(QMainWindow):
         elif index == ModbusConnection.TCP:
             self._modbus_device = QModbusTcpClient(self)
             if not self.ui.portEdit.text():
-                self.ui.portEdit.setText("127.0.0.1:502")
+                self.ui.portEdit.setText("127.0.0.1:50200")
 
         self._modbus_device.errorOccurred.connect(self._show_device_errorstring)
 
@@ -133,24 +133,24 @@ class MainWindow(QMainWindow):
 
         self.statusBar().clearMessage()
         md = self._modbus_device
-        if md.state() != QModbusDevice.ConnectedState:
+        if md.state() != QModbusDevice.State.ConnectedState:
             settings = self._settings_dialog.settings()
             if self.ui.connectType.currentIndex() == ModbusConnection.SERIAL:
-                md.setConnectionParameter(QModbusDevice.SerialPortNameParameter,
+                md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialPortNameParameter,
                                           self.ui.portEdit.text())
-                md.setConnectionParameter(QModbusDevice.SerialParityParameter,
+                md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialParityParameter,
                                           settings.parity)
-                md.setConnectionParameter(QModbusDevice.SerialBaudRateParameter,
+                md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialBaudRateParameter,
                                           settings.baud)
-                md.setConnectionParameter(QModbusDevice.SerialDataBitsParameter,
+                md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialDataBitsParameter,
                                           settings.data_bits)
-                md.setConnectionParameter(QModbusDevice.SerialStopBitsParameter,
+                md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialStopBitsParameter,
                                           settings.stop_bits)
             else:
                 url = QUrl.fromUserInput(self.ui.portEdit.text())
-                md.setConnectionParameter(QModbusDevice.NetworkPortParameter,
+                md.setConnectionParameter(QModbusDevice.ConnectionParameter.NetworkPortParameter,
                                           url.port())
-                md.setConnectionParameter(QModbusDevice.NetworkAddressParameter,
+                md.setConnectionParameter(QModbusDevice.ConnectionParameter.NetworkAddressParameter,
                                           url.host())
 
             md.setTimeout(settings.response_time)
@@ -169,13 +169,13 @@ class MainWindow(QMainWindow):
 
     @Slot(int)
     def onModbusStateChanged(self, state):
-        connected = (state != QModbusDevice.UnconnectedState)
+        connected = (state != QModbusDevice.State.UnconnectedState)
         self.ui.actionConnect.setEnabled(not connected)
         self.ui.actionDisconnect.setEnabled(connected)
 
-        if state == QModbusDevice.UnconnectedState:
+        if state == QModbusDevice.State.UnconnectedState:
             self.ui.connectButton.setText("Connect")
-        elif state == QModbusDevice.ConnectedState:
+        elif state == QModbusDevice.State.ConnectedState:
             self.ui.connectButton.setText("Disconnect")
 
     @Slot()
@@ -188,7 +188,7 @@ class MainWindow(QMainWindow):
                                                     self.ui.serverEdit.value())
         if reply:
             if not reply.isFinished():
-                reply.finished.connect(functools.partial(self.onReadReady, reply))
+                reply.finished.connect(self.onReadReady)
             else:
                 del reply  # broadcast replies return immediately
         else:
@@ -196,7 +196,8 @@ class MainWindow(QMainWindow):
             self.statusBar().showMessage(message, 5000)
 
     @Slot()
-    def onReadReady(self, reply):
+    def onReadReady(self):
+        reply = self.sender()
         if not reply:
             return
 
@@ -236,7 +237,7 @@ class MainWindow(QMainWindow):
         table = write_unit.registerType()
         for i in range(0, total):
             addr = i + write_unit.startAddress()
-            if table == QModbusDataUnit.Coils:
+            if table == QModbusDataUnit.RegisterType.Coils:
                 write_unit.setValue(i, self._write_model.m_coils[addr])
             else:
                 write_unit.setValue(i, self._write_model.m_holdingRegisters[addr])
@@ -248,13 +249,16 @@ class MainWindow(QMainWindow):
                 # broadcast replies return immediately
                 reply.deleteLater()
             else:
-                reply.finished.connect(functools.partial(self._write_finished, reply))
+                reply.finished.connect(self._write_finished)
         else:
             message = "Write error: " + self._modbus_device.errorString()
             self.statusBar().showMessage(message, 5000)
 
-    @Slot(QModbusReply)
-    def _write_finished(self, reply):
+    @Slot()
+    def _write_finished(self):
+        reply = self.sender()
+        if not reply:
+            return
         error = reply.error()
         if error == QModbusDevice.ProtocolError:
             e = reply.errorString()
@@ -289,7 +293,7 @@ class MainWindow(QMainWindow):
                                                          self.ui.serverEdit.value())
         if reply:
             if not reply.isFinished():
-                reply.finished.connect(functools.partial(self.onReadReady, reply))
+                reply.finished.connect(self.onReadReady)
             else:
                 del reply  # broadcast replies return immediately
         else:
index 1de3819784d4cae6f83db8b89a9198680a252b59..0681b99e027dbd8e9d4371c1a5de8d5f2aec3927 100644 (file)
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>516</width>
+    <width>601</width>
     <height>378</height>
    </rect>
   </property>
@@ -61,7 +61,7 @@
       <item row="0" column="4">
        <spacer name="horizontalSpacer">
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Orientation::Horizontal</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
       <item>
        <spacer name="horizontalSpacer_2">
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Orientation::Horizontal</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>516</width>
-     <height>21</height>
+     <width>601</width>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuDevice">
index a5e4a37ca803c0bb269373504d987379f2fb6920..d86ea05d26b315bd05d71b7580dfe071e3e37880 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtWidgets import QDialog
@@ -10,10 +11,10 @@ from ui_settingsdialog import Ui_SettingsDialog
 
 class Settings:
     def __init__(self):
-        self.parity = QSerialPort.EvenParity
-        self.baud = QSerialPort.Baud19200
-        self.data_bits = QSerialPort.Data8
-        self.stop_bits = QSerialPort.OneStop
+        self.parity = QSerialPort.Parity.EvenParity
+        self.baud = QSerialPort.BaudRate.Baud19200
+        self.data_bits = QSerialPort.DataBits.Data8
+        self.stop_bits = QSerialPort.StopBits.OneStop
         self.response_time = 1000
         self.number_of_retries = 3
 
index fb594f4d4ba79abad0e525516699d11ef2bba0b0..68122a1e8e1e6afdcbe693e2955b4646ee07bee4 100644 (file)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>239</width>
-    <height>256</height>
+    <width>259</width>
+    <height>321</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -17,7 +17,7 @@
    <item row="3" column="1">
     <spacer name="verticalSpacer">
      <property name="orientation">
-      <enum>Qt::Vertical</enum>
+      <enum>Qt::Orientation::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
index 1535fc8d8570937f351898947c798321363d0d30..a23e5fc68b3142f622d23769362b3c8b262492fa 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'mainwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -28,27 +28,27 @@ class Ui_MainWindow(object):
     def setupUi(self, MainWindow):
         if not MainWindow.objectName():
             MainWindow.setObjectName(u"MainWindow")
-        MainWindow.resize(516, 378)
+        MainWindow.resize(601, 378)
         MainWindow.setMaximumSize(QSize(16777215, 1000))
         self.actionConnect = QAction(MainWindow)
         self.actionConnect.setObjectName(u"actionConnect")
         icon = QIcon()
-        icon.addFile(u":/images/connect.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon.addFile(u":/images/connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionConnect.setIcon(icon)
         self.actionDisconnect = QAction(MainWindow)
         self.actionDisconnect.setObjectName(u"actionDisconnect")
         icon1 = QIcon()
-        icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionDisconnect.setIcon(icon1)
         self.actionExit = QAction(MainWindow)
         self.actionExit.setObjectName(u"actionExit")
         icon2 = QIcon()
-        icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionExit.setIcon(icon2)
         self.actionOptions = QAction(MainWindow)
         self.actionOptions.setObjectName(u"actionOptions")
         icon3 = QIcon()
-        icon3.addFile(u":/images/settings.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon3.addFile(u":/images/settings.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionOptions.setIcon(icon3)
         self.centralWidget = QWidget(MainWindow)
         self.centralWidget.setObjectName(u"centralWidget")
@@ -195,7 +195,7 @@ class Ui_MainWindow(object):
 
         self.writeValueTable = QTreeView(self.writeGroupBox)
         self.writeValueTable.setObjectName(u"writeValueTable")
-        self.writeValueTable.setProperty("showDropIndicator", True)
+        self.writeValueTable.setProperty(u"showDropIndicator", True)
         self.writeValueTable.setAlternatingRowColors(True)
         self.writeValueTable.setRootIsDecorated(False)
         self.writeValueTable.setUniformRowHeights(True)
@@ -286,7 +286,7 @@ class Ui_MainWindow(object):
         MainWindow.setStatusBar(self.statusBar)
         self.menuBar = QMenuBar(MainWindow)
         self.menuBar.setObjectName(u"menuBar")
-        self.menuBar.setGeometry(QRect(0, 0, 516, 21))
+        self.menuBar.setGeometry(QRect(0, 0, 601, 26))
         self.menuDevice = QMenu(self.menuBar)
         self.menuDevice.setObjectName(u"menuDevice")
         self.menuToo_ls = QMenu(self.menuBar)
index af3a0665c7e9257be44f573dec82211d9816cdd1..0dfda7a0122c6390a1258b56764593a8916f6069 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'settingsdialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -23,7 +23,7 @@ class Ui_SettingsDialog(object):
     def setupUi(self, SettingsDialog):
         if not SettingsDialog.objectName():
             SettingsDialog.setObjectName(u"SettingsDialog")
-        SettingsDialog.resize(239, 256)
+        SettingsDialog.resize(259, 321)
         self.gridLayout = QGridLayout(SettingsDialog)
         self.gridLayout.setObjectName(u"gridLayout")
         self.verticalSpacer = QSpacerItem(20, 43, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
index 07b1226de36dc2332e6767c22bada91ffe126c47..17b61bcac5421b71cb4338a11c9d27514515150c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import IntEnum
 
@@ -40,23 +41,23 @@ class WriteRegisterModel(QAbstractTableModel):
         assert self.m_coils.size() == Column.ROW_COUNT
         assert len(self.m_holdingRegisters) == Column.ROW_COUNT
 
-        if column == Column.NUM_COLUMN and role == Qt.DisplayRole:
+        if column == Column.NUM_COLUMN and role == Qt.ItemDataRole.DisplayRole:
             return f"{row}"
 
-        if column == Column.COILS_COLUMN and role == Qt.CheckStateRole:  # coils
-            return Qt.Checked if self.m_coils[row] else Qt.Unchecked
+        if column == Column.COILS_COLUMN and role == Qt.ItemDataRole.CheckStateRole:  # coils
+            return Qt.CheckState.Checked if self.m_coils[row] else Qt.CheckState.Unchecked
 
         # holding registers
-        if column == Column.HOLDING_COLUMN and role == Qt.DisplayRole:
+        if column == Column.HOLDING_COLUMN and role == Qt.ItemDataRole.DisplayRole:
             reg = self.m_holdingRegisters[row]
             return f"0x{reg:x}"
         return None
 
     def headerData(self, section, orientation, role):
-        if role != Qt.DisplayRole:
+        if role != Qt.ItemDataRole.DisplayRole:
             return None
 
-        if orientation == Qt.Horizontal:
+        if orientation == Qt.Orientation.Horizontal:
             if section == Column.NUM_COLUMN:
                 return "#"
             if section == Column.COILS_COLUMN:
@@ -74,16 +75,17 @@ class WriteRegisterModel(QAbstractTableModel):
         assert self.m_coils.size() == Column.ROW_COUNT
         assert len(self.m_holdingRegisters) == Column.ROW_COUNT
 
-        if column == Column.COILS_COLUMN and role == Qt.CheckStateRole:  # coils
+        if column == Column.COILS_COLUMN and role == Qt.ItemDataRole.CheckStateRole:  # coils
             s = Qt.CheckState(int(value))
-            if s == Qt.Checked:
+            if s == Qt.CheckState.Checked:
                 self.m_coils.setBit(row)
             else:
                 self.m_coils.clearBit(row)
             self.dataChanged.emit(index, index)
             return True
 
-        if column == Column.HOLDING_COLUMN and role == Qt.EditRole:  # holding registers
+        if column == Column.HOLDING_COLUMN and role == Qt.ItemDataRole.EditRole:
+            # holding registers
             base = 16 if value.startswith("0x") else 10
             self.m_holdingRegisters[row] = int(value, base=base)
             self.dataChanged.emit(index, index)
@@ -99,12 +101,12 @@ class WriteRegisterModel(QAbstractTableModel):
             return flags
 
         if row < self.m_address or row >= (self.m_address + self.m_number):
-            flags &= ~Qt.ItemIsEnabled
+            flags &= ~Qt.ItemFlag.ItemIsEnabled
 
         if column == Column.COILS_COLUMN:  # coils
-            return flags | Qt.ItemIsUserCheckable
+            return flags | Qt.ItemFlag.ItemIsUserCheckable
         if column == Column.HOLDING_COLUMN:  # holding registers
-            return flags | Qt.ItemIsEditable
+            return flags | Qt.ItemFlag.ItemIsEditable
         return flags
 
     @Slot(int)
index 125951374236e78712b1ff040d8b1a7a0df5ad8f..682711f4ec5e0b423e95d9b6f5a9704571da73c5 100644 (file)
@@ -1,13 +1,14 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, Signal, Slot
 from PySide6.QtGui import QPalette
 from PySide6.QtWidgets import QPlainTextEdit
 
 
-UNHANDLED_KEYS = [Qt.Key_Backspace, Qt.Key_Left, Qt.Key_Right, Qt.Key_Up,
-                  Qt.Key_Down]
+UNHANDLED_KEYS = [Qt.Key.Key_Backspace, Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up,
+                  Qt.Key.Key_Down]
 
 
 class Console(QPlainTextEdit):
@@ -19,8 +20,8 @@ class Console(QPlainTextEdit):
         self.m_localEchoEnabled = False
         self.document().setMaximumBlockCount(100)
         p = self.palette()
-        p.setColor(QPalette.Base, Qt.black)
-        p.setColor(QPalette.Text, Qt.green)
+        p.setColor(QPalette.ColorRole.Base, Qt.GlobalColor.black)
+        p.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.green)
         self.setPalette(p)
 
     @Slot(bytearray)
index 0d2ce0a01af87f91261b9c5e8960aa5d2f4fe560..feff5da1a86392d3625f4a0fbf12121384f7eea0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index bdfb0fb0ea2dc582f68d78dfd1424a62afe77a5f..557821a764f176701664fc5fb58c4ce907d4474d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QIODeviceBase, Slot
 from PySide6.QtWidgets import QLabel, QMainWindow, QMessageBox
@@ -62,7 +63,7 @@ class MainWindow(QMainWindow):
         self.m_serial.setParity(s.parity)
         self.m_serial.setStopBits(s.stop_bits)
         self.m_serial.setFlowControl(s.flow_control)
-        if self.m_serial.open(QIODeviceBase.ReadWrite):
+        if self.m_serial.open(QIODeviceBase.OpenModeFlag.ReadWrite):
             self.m_console.setEnabled(True)
             self.m_console.set_local_echo_enabled(s.local_echo_enabled)
             self.m_ui.actionConnect.setEnabled(False)
@@ -98,7 +99,7 @@ class MainWindow(QMainWindow):
 
     @Slot(QSerialPort.SerialPortError)
     def handle_error(self, error):
-        if error == QSerialPort.ResourceError:
+        if error == QSerialPort.SerialPortError.ResourceError:
             QMessageBox.critical(self, "Critical Error",
                                  self.m_serial.errorString())
             self.close_serial_port()
index 452fdd537bd0010ebd51b8806e46dc2580923dc4..858736e3183aebe0f6db83a2933039519816cc6d 100644 (file)
@@ -22,7 +22,7 @@
      <x>0</x>
      <y>0</y>
      <width>400</width>
-     <height>19</height>
+     <height>26</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuCalls">
index c9373d5b00855badd6d5dfe0142e16eb3f044041..e950c81cad3374c35ea0178acd6510a2f5fce73d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -25,13 +26,13 @@ class Settings():
         self.name = ""
         self.baud_rate = 0
         self.string_baud_rate = ""
-        self.data_bits = QSerialPort.Data8
+        self.data_bits = QSerialPort.DataBits.Data8
         self.string_data_bits = ""
-        self.parity = QSerialPort.NoParity
+        self.parity = QSerialPort.Parity.NoParity
         self.string_parity = ""
-        self.stop_bits = QSerialPort.OneStop
+        self.stop_bits = QSerialPort.StopBits.OneStop
         self.string_stop_bits = ""
-        self.flow_control = QSerialPort.SoftwareControl
+        self.flow_control = QSerialPort.FlowControl.SoftwareControl
         self.string_flow_control = ""
         self.local_echo_enabled = False
 
@@ -47,7 +48,7 @@ class SettingsDialog(QDialog):
         self.m_currentSettings = Settings()
         self.m_intValidator = QIntValidator(0, 4000000, self)
 
-        self.m_ui.baudRateBox.setInsertPolicy(QComboBox.NoInsert)
+        self.m_ui.baudRateBox.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
 
         self.m_ui.applyButton.clicked.connect(self.apply)
         self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(self.show_port_info)
@@ -105,33 +106,33 @@ class SettingsDialog(QDialog):
             self.m_ui.serialPortInfoListBox.clearEditText()
 
     def fill_ports_parameters(self):
-        self.m_ui.baudRateBox.addItem("9600", QSerialPort.Baud9600)
-        self.m_ui.baudRateBox.addItem("19200", QSerialPort.Baud19200)
-        self.m_ui.baudRateBox.addItem("38400", QSerialPort.Baud38400)
-        self.m_ui.baudRateBox.addItem("115200", QSerialPort.Baud115200)
+        self.m_ui.baudRateBox.addItem("9600", QSerialPort.BaudRate.Baud9600)
+        self.m_ui.baudRateBox.addItem("19200", QSerialPort.BaudRate.Baud19200)
+        self.m_ui.baudRateBox.addItem("38400", QSerialPort.BaudRate.Baud38400)
+        self.m_ui.baudRateBox.addItem("115200", QSerialPort.BaudRate.Baud115200)
         self.m_ui.baudRateBox.addItem("Custom")
 
-        self.m_ui.dataBitsBox.addItem("5", QSerialPort.Data5)
-        self.m_ui.dataBitsBox.addItem("6", QSerialPort.Data6)
-        self.m_ui.dataBitsBox.addItem("7", QSerialPort.Data7)
-        self.m_ui.dataBitsBox.addItem("8", QSerialPort.Data8)
+        self.m_ui.dataBitsBox.addItem("5", QSerialPort.DataBits.Data5)
+        self.m_ui.dataBitsBox.addItem("6", QSerialPort.DataBits.Data6)
+        self.m_ui.dataBitsBox.addItem("7", QSerialPort.DataBits.Data7)
+        self.m_ui.dataBitsBox.addItem("8", QSerialPort.DataBits.Data8)
         self.m_ui.dataBitsBox.setCurrentIndex(3)
 
-        self.m_ui.parityBox.addItem("None", QSerialPort.NoParity)
-        self.m_ui.parityBox.addItem("Even", QSerialPort.EvenParity)
-        self.m_ui.parityBox.addItem("Odd", QSerialPort.OddParity)
-        self.m_ui.parityBox.addItem("Mark", QSerialPort.MarkParity)
-        self.m_ui.parityBox.addItem("Space", QSerialPort.SpaceParity)
+        self.m_ui.parityBox.addItem("None", QSerialPort.Parity.NoParity)
+        self.m_ui.parityBox.addItem("Even", QSerialPort.Parity.EvenParity)
+        self.m_ui.parityBox.addItem("Odd", QSerialPort.Parity.OddParity)
+        self.m_ui.parityBox.addItem("Mark", QSerialPort.Parity.MarkParity)
+        self.m_ui.parityBox.addItem("Space", QSerialPort.Parity.SpaceParity)
 
-        self.m_ui.stopBitsBox.addItem("1", QSerialPort.OneStop)
+        self.m_ui.stopBitsBox.addItem("1", QSerialPort.StopBits.OneStop)
         if sys.platform == "win32":
-            self.m_ui.stopBitsBox.addItem("1.5", QSerialPort.OneAndHalfStop)
+            self.m_ui.stopBitsBox.addItem("1.5", QSerialPort.StopBits.OneAndHalfStop)
 
-        self.m_ui.stopBitsBox.addItem("2", QSerialPort.TwoStop)
+        self.m_ui.stopBitsBox.addItem("2", QSerialPort.StopBits.TwoStop)
 
-        self.m_ui.flowControlBox.addItem("None", QSerialPort.NoFlowControl)
-        self.m_ui.flowControlBox.addItem("RTS/CTS", QSerialPort.HardwareControl)
-        self.m_ui.flowControlBox.addItem("XON/XOFF", QSerialPort.SoftwareControl)
+        self.m_ui.flowControlBox.addItem("None", QSerialPort.FlowControl.NoFlowControl)
+        self.m_ui.flowControlBox.addItem("RTS/CTS", QSerialPort.FlowControl.HardwareControl)
+        self.m_ui.flowControlBox.addItem("XON/XOFF", QSerialPort.FlowControl.SoftwareControl)
 
     def fill_ports_info(self):
         self.m_ui.serialPortInfoListBox.clear()
index 8f15b1a527050811b7237a9d2bc058cf8788b77d..eedd4ccc9c834cdc58b5f59be4b1de2c51071f83 100644 (file)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>281</width>
-    <height>262</height>
+    <width>392</width>
+    <height>386</height>
    </rect>
   </property>
   <property name="windowTitle">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
-        <enum>Qt::Horizontal</enum>
+        <enum>Qt::Orientation::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
index 21adbd5c12274e281f098c12b7c75508df92958f..e9f7cd97f395a56c3d4468d47bf2078d72c5b2fc 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'mainwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -33,27 +33,27 @@ class Ui_MainWindow(object):
         self.actionConnect = QAction(MainWindow)
         self.actionConnect.setObjectName(u"actionConnect")
         icon = QIcon()
-        icon.addFile(u":/images/connect.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon.addFile(u":/images/connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionConnect.setIcon(icon)
         self.actionDisconnect = QAction(MainWindow)
         self.actionDisconnect.setObjectName(u"actionDisconnect")
         icon1 = QIcon()
-        icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionDisconnect.setIcon(icon1)
         self.actionConfigure = QAction(MainWindow)
         self.actionConfigure.setObjectName(u"actionConfigure")
         icon2 = QIcon()
-        icon2.addFile(u":/images/settings.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon2.addFile(u":/images/settings.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionConfigure.setIcon(icon2)
         self.actionClear = QAction(MainWindow)
         self.actionClear.setObjectName(u"actionClear")
         icon3 = QIcon()
-        icon3.addFile(u":/images/clear.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon3.addFile(u":/images/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionClear.setIcon(icon3)
         self.actionQuit = QAction(MainWindow)
         self.actionQuit.setObjectName(u"actionQuit")
         icon4 = QIcon()
-        icon4.addFile(u":/images/application-exit.png", QSize(), QIcon.Normal, QIcon.Off)
+        icon4.addFile(u":/images/application-exit.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
         self.actionQuit.setIcon(icon4)
         self.centralWidget = QWidget(MainWindow)
         self.centralWidget.setObjectName(u"centralWidget")
@@ -64,7 +64,7 @@ class Ui_MainWindow(object):
         MainWindow.setCentralWidget(self.centralWidget)
         self.menuBar = QMenuBar(MainWindow)
         self.menuBar.setObjectName(u"menuBar")
-        self.menuBar.setGeometry(QRect(0, 0, 400, 19))
+        self.menuBar.setGeometry(QRect(0, 0, 400, 26))
         self.menuCalls = QMenu(self.menuBar)
         self.menuCalls.setObjectName(u"menuCalls")
         self.menuTools = QMenu(self.menuBar)
index 6c5a417cc64d2f2923a14c2bc07f8e31e9d81252..4429322bdec2727c3c1f01cee7ca4482b835fc36 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'settingsdialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -24,7 +24,7 @@ class Ui_SettingsDialog(object):
     def setupUi(self, SettingsDialog):
         if not SettingsDialog.objectName():
             SettingsDialog.setObjectName(u"SettingsDialog")
-        SettingsDialog.resize(281, 262)
+        SettingsDialog.resize(392, 386)
         self.gridLayout_3 = QGridLayout(SettingsDialog)
         self.gridLayout_3.setObjectName(u"gridLayout_3")
         self.parametersBox = QGroupBox(SettingsDialog)
index 19b6c3bbd84bda86164bcc40b743f047b18790b2..558451e8a4e18b166ebea9db2ff0cefd540030d9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 import sys
@@ -34,42 +35,42 @@ class AudioWidget(QWidget):
         file_layout.addWidget(self._file_dialog_button)
         form.addRow(file_layout)
 
-        self._azimuth = QSlider(Qt.Horizontal)
+        self._azimuth = QSlider(Qt.Orientation.Horizontal)
         self._azimuth.setRange(-180, 180)
         form.addRow("Azimuth (-180 - 180 degree):", self._azimuth)
 
-        self._elevation = QSlider(Qt.Horizontal)
+        self._elevation = QSlider(Qt.Orientation.Horizontal)
         self._elevation.setRange(-90, 90)
         form.addRow("Elevation (-90 - 90 degree)", self._elevation)
 
-        self._distance = QSlider(Qt.Horizontal)
+        self._distance = QSlider(Qt.Orientation.Horizontal)
         self._distance.setRange(0, 1000)
         self._distance.setValue(100)
         form.addRow("Distance (0 - 10 meter):", self._distance)
 
-        self._occlusion = QSlider(Qt.Horizontal)
+        self._occlusion = QSlider(Qt.Orientation.Horizontal)
         self._occlusion.setRange(0, 400)
         form.addRow("Occlusion (0 - 4):", self._occlusion)
 
-        self._room_dimension = QSlider(Qt.Horizontal)
+        self._room_dimension = QSlider(Qt.Orientation.Horizontal)
         self._room_dimension.setRange(0, 10000)
         self._room_dimension.setValue(1000)
         form.addRow("Room dimension (0 - 100 meter):", self._room_dimension)
 
-        self._reverb_gain = QSlider(Qt.Horizontal)
+        self._reverb_gain = QSlider(Qt.Orientation.Horizontal)
         self._reverb_gain.setRange(0, 500)
         self._reverb_gain.setValue(0)
         form.addRow("Reverb gain (0-5):", self._reverb_gain)
 
-        self._reflection_gain = QSlider(Qt.Horizontal)
+        self._reflection_gain = QSlider(Qt.Orientation.Horizontal)
         self._reflection_gain.setRange(0, 500)
         self._reflection_gain.setValue(0)
         form.addRow("Reflection gain (0-5):", self._reflection_gain)
 
         self._mode = QComboBox()
-        self._mode.addItem("Surround", QAudioEngine.Surround)
-        self._mode.addItem("Stereo", QAudioEngine.Stereo)
-        self._mode.addItem("Headphone", QAudioEngine.Headphone)
+        self._mode.addItem("Surround", QAudioEngine.OutputMode.Surround)
+        self._mode.addItem("Stereo", QAudioEngine.OutputMode.Stereo)
+        self._mode.addItem("Headphone", QAudioEngine.OutputMode.Headphone)
 
         form.addRow("Output mode:", self._mode)
 
@@ -92,12 +93,12 @@ class AudioWidget(QWidget):
 
         self._engine = QAudioEngine()
         self._room = QAudioRoom(self._engine)
-        self._room.setWallMaterial(QAudioRoom.BackWall, QAudioRoom.BrickBare)
-        self._room.setWallMaterial(QAudioRoom.FrontWall, QAudioRoom.BrickBare)
-        self._room.setWallMaterial(QAudioRoom.LeftWall, QAudioRoom.BrickBare)
-        self._room.setWallMaterial(QAudioRoom.RightWall, QAudioRoom.BrickBare)
-        self._room.setWallMaterial(QAudioRoom.Floor, QAudioRoom.Marble)
-        self._room.setWallMaterial(QAudioRoom.Ceiling, QAudioRoom.WoodCeiling)
+        self._room.setWallMaterial(QAudioRoom.Wall.BackWall, QAudioRoom.Material.BrickBare)
+        self._room.setWallMaterial(QAudioRoom.Wall.FrontWall, QAudioRoom.Material.BrickBare)
+        self._room.setWallMaterial(QAudioRoom.Wall.LeftWall, QAudioRoom.Material.BrickBare)
+        self._room.setWallMaterial(QAudioRoom.Wall.RightWall, QAudioRoom.Material.BrickBare)
+        self._room.setWallMaterial(QAudioRoom.Wall.Floor, QAudioRoom.Material.Marble)
+        self._room.setWallMaterial(QAudioRoom.Wall.Ceiling, QAudioRoom.Material.WoodCeiling)
         self.update_room()
 
         self._listener = QAudioListener(self._engine)
@@ -140,20 +141,20 @@ class AudioWidget(QWidget):
     def file_changed(self, file):
         self._sound.setSource(QUrl.fromLocalFile(file))
         self._sound.setSize(5)
-        self._sound.setLoops(QSpatialSound.Infinite)
+        self._sound.setLoops(QSpatialSound.Loops.Infinite)
 
     @Slot()
     def open_file_dialog(self):
         if not self._file_dialog:
-            directory = QStandardPaths.writableLocation(QStandardPaths.MusicLocation)
+            directory = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.MusicLocation)  # noqa: E501
             self._file_dialog = QFileDialog(self, "Open Audio File", directory)
-            self._file_dialog.setAcceptMode(QFileDialog.AcceptOpen)
+            self._file_dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
             mime_types = ["audio/mpeg", "audio/aac", "audio/x-ms-wma",
                           "audio/x-flac+ogg", "audio/x-wav"]
             self._file_dialog.setMimeTypeFilters(mime_types)
             self._file_dialog.selectMimeTypeFilter(mime_types[0])
 
-        if self._file_dialog.exec() == QDialog.Accepted:
+        if self._file_dialog.exec() == QDialog.DialogCode.Accepted:
             self._file_edit.setText(self._file_dialog.selectedFiles()[0])
 
     @Slot()
index b7c17c35f13f2eb7b15db8e66510fc6aafb0d6d8..8a2fa88b9332d29bec6d114c80b9efeff7d5d83e 100644 (file)
@@ -13,4 +13,3 @@ voice.
 .. image:: hello_speak.webp
    :width: 400
    :alt: Hello Speak Screenshot
-
index b025dd6c0b5aca33cf918ee235a46b1218e4dfc8..309e7ef1ba39ca640e5b6ddab761315670e394d5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the hello_speak example from Qt v6.x"""
 
index 7790fec8cadef683cbe842b9fe9ea5bb4b3aca78..cb46b55f31aea8ee42cf5f572adf18b902f198ad 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QLocale, QSignalBlocker, Slot
 from PySide6.QtWidgets import QMainWindow
index 6f3accf6cf196d3680cdf25508c51a7a1a04d129..6c328bcc30f7ff213d289e852746b13aa5d8461d 100644 (file)
@@ -46,7 +46,7 @@ Done, over and out.</string>
          <string>Engine</string>
         </property>
         <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
         </property>
        </widget>
       </item>
@@ -62,7 +62,7 @@ Done, over and out.</string>
          <string>Pitch:</string>
         </property>
         <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
         </property>
        </widget>
       </item>
@@ -78,7 +78,7 @@ Done, over and out.</string>
          <string>&amp;Language:</string>
         </property>
         <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
         </property>
         <property name="buddy">
          <cstring>language</cstring>
@@ -97,7 +97,7 @@ Done, over and out.</string>
          <number>1</number>
         </property>
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Orientation::Horizontal</enum>
         </property>
        </widget>
       </item>
@@ -107,7 +107,7 @@ Done, over and out.</string>
          <string>Voice name:</string>
         </property>
         <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
         </property>
        </widget>
       </item>
@@ -126,7 +126,7 @@ Done, over and out.</string>
          <number>70</number>
         </property>
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Orientation::Horizontal</enum>
         </property>
        </widget>
       </item>
@@ -155,7 +155,7 @@ Done, over and out.</string>
          <string>Rate:</string>
         </property>
         <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
         </property>
        </widget>
       </item>
@@ -171,7 +171,7 @@ Done, over and out.</string>
          <string>Volume:</string>
         </property>
         <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
         </property>
        </widget>
       </item>
@@ -184,7 +184,7 @@ Done, over and out.</string>
          <number>10</number>
         </property>
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Orientation::Horizontal</enum>
         </property>
        </widget>
       </item>
@@ -241,7 +241,7 @@ Done, over and out.</string>
     <item>
      <spacer name="verticalSpacer">
       <property name="orientation">
-       <enum>Qt::Vertical</enum>
+       <enum>Qt::Orientation::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
index ab132e7fb2bb5eb25912ee2632385ff09acf532d..c7c4cb62d1f51c7b1eaf90c85754bf5bd2d54d90 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'mainwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -48,7 +48,7 @@ class Ui_MainWindow(object):
         sizePolicy1.setVerticalStretch(0)
         sizePolicy1.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
         self.label_5.setSizePolicy(sizePolicy1)
-        self.label_5.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.label_5.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1)
 
@@ -56,7 +56,7 @@ class Ui_MainWindow(object):
         self.label_3.setObjectName(u"label_3")
         sizePolicy1.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
         self.label_3.setSizePolicy(sizePolicy1)
-        self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.label_3.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout.addWidget(self.label_3, 3, 0, 1, 1)
 
@@ -64,7 +64,7 @@ class Ui_MainWindow(object):
         self.label_4.setObjectName(u"label_4")
         sizePolicy1.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
         self.label_4.setSizePolicy(sizePolicy1)
-        self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.label_4.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout.addWidget(self.label_4, 5, 0, 1, 1)
 
@@ -73,13 +73,13 @@ class Ui_MainWindow(object):
         self.pitch.setMinimum(-10)
         self.pitch.setMaximum(10)
         self.pitch.setSingleStep(1)
-        self.pitch.setOrientation(Qt.Horizontal)
+        self.pitch.setOrientation(Qt.Orientation.Horizontal)
 
         self.gridLayout.addWidget(self.pitch, 3, 2, 1, 1)
 
         self.label_6 = QLabel(self.centralwidget)
         self.label_6.setObjectName(u"label_6")
-        self.label_6.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.label_6.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout.addWidget(self.label_6, 6, 0, 1, 1)
 
@@ -89,7 +89,7 @@ class Ui_MainWindow(object):
         self.volume.setSingleStep(5)
         self.volume.setPageStep(20)
         self.volume.setValue(70)
-        self.volume.setOrientation(Qt.Horizontal)
+        self.volume.setOrientation(Qt.Orientation.Horizontal)
 
         self.gridLayout.addWidget(self.volume, 1, 2, 1, 1)
 
@@ -112,7 +112,7 @@ class Ui_MainWindow(object):
         self.label.setObjectName(u"label")
         sizePolicy1.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
         self.label.setSizePolicy(sizePolicy1)
-        self.label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.label.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout.addWidget(self.label, 2, 0, 1, 1)
 
@@ -120,7 +120,7 @@ class Ui_MainWindow(object):
         self.label_2.setObjectName(u"label_2")
         sizePolicy1.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
         self.label_2.setSizePolicy(sizePolicy1)
-        self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+        self.label_2.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
 
         self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
 
@@ -128,7 +128,7 @@ class Ui_MainWindow(object):
         self.rate.setObjectName(u"rate")
         self.rate.setMinimum(-10)
         self.rate.setMaximum(10)
-        self.rate.setOrientation(Qt.Horizontal)
+        self.rate.setOrientation(Qt.Orientation.Horizontal)
 
         self.gridLayout.addWidget(self.rate, 2, 2, 1, 1)
 
index 78295adf16a77af60bc52370eee63c907b19b1ab..897d0a63a517d71d895f48ff8d257a75ccffd13c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import copy
 from PySide6.QtSql import QSqlRelationalDelegate
@@ -45,7 +46,7 @@ class BookDelegate(QSqlRelationalDelegate):
             if option.state & QStyle.State_Selected:
                 painter.fillRect(option.rect,
                                  option.palette.color(color_group, QPalette.Highlight))
-            rating = model.data(index, Qt.DisplayRole)
+            rating = model.data(index, Qt.ItemDataRole.DisplayRole)
             width = self.star.width()
             height = self.star.height()
             x = option.rect.x()
index 7f9e0f94b8e0ed9fde99ef25a797a80003018732..e913a228cfef62f581b071bac8507803c28fabcf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
                                QHeaderView, QMainWindow, QMessageBox)
@@ -34,11 +35,12 @@ class BookWindow(QMainWindow, Ui_BookWindow):
         model.setRelation(genre_idx, QSqlRelation("genres", "id", "name"))
 
         # Set the localized header captions:
-        model.setHeaderData(author_idx, Qt.Horizontal, self.tr("Author Name"))
-        model.setHeaderData(genre_idx, Qt.Horizontal, self.tr("Genre"))
-        model.setHeaderData(model.fieldIndex("title"), Qt.Horizontal, self.tr("Title"))
-        model.setHeaderData(model.fieldIndex("year"), Qt.Horizontal, self.tr("Year"))
-        model.setHeaderData(model.fieldIndex("rating"), Qt.Horizontal, self.tr("Rating"))
+        model.setHeaderData(author_idx, Qt.Orientation.Horizontal, self.tr("Author Name"))
+        model.setHeaderData(genre_idx, Qt.Orientation.Horizontal, self.tr("Genre"))
+        model.setHeaderData(model.fieldIndex("title"), Qt.Orientation.Horizontal, self.tr("Title"))
+        model.setHeaderData(model.fieldIndex("year"), Qt.Orientation.Horizontal, self.tr("Year"))
+        model.setHeaderData(model.fieldIndex("rating"),
+                            Qt.Orientation.Horizontal, self.tr("Rating"))
 
         if not model.select():
             print(model.lastError())
index ce8f9f933afee581d23fe41063fea8e7ab6dd245..ecd458ab4b9c334b4c53e2e1369bbc0a57f64ecc 100644 (file)
@@ -54,7 +54,7 @@
        <item>
         <widget class="QTableView" name="bookTable">
          <property name="selectionBehavior">
-          <enum>QAbstractItemView::SelectRows</enum>
+          <enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
          </property>
         </widget>
        </item>
index 5ee0f148ed5aff3f413f4eef6bfd5c755e54be6c..471148c611365937306d288cee271da2fc09a798 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtSql import QSqlDatabase, QSqlQuery
 from datetime import date
index 025b5588471a81482c2197eaeff6d1caaa4c0603..4b4aee1a319048e795c8ccf8ff55d06eceb3187a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import QApplication
index 52795217e285ac92b3081023f5cad63c685996cd..2244dd7d6787ae0dd749e6aade59b21bf2200c67 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'bookwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -39,7 +39,7 @@ class Ui_BookWindow(object):
         self.vboxLayout1.setContentsMargins(9, 9, 9, 9)
         self.bookTable = QTableView(self.groupBox)
         self.bookTable.setObjectName(u"bookTable")
-        self.bookTable.setSelectionBehavior(QAbstractItemView.SelectRows)
+        self.bookTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
 
         self.vboxLayout1.addWidget(self.bookTable)
 
index 6bfc828c8dfede058bb4ad30d85293088589ec21..61c154ba42a51d470327e52595be2aabb0fa4374 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the relationaltablemodel example from Qt v6.x"""
 
index d2efac2dc9d33f0162bf9c33d1d85f79f4dea207..47328e7288c668568cb693fa75e11c66829b284a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the relationaltablemodel example from Qt v6.x"""
 
@@ -19,11 +20,11 @@ def initializeModel(model):
     model.setEditStrategy(QSqlRelationalTableModel.OnManualSubmit)
     model.setRelation(2, QSqlRelation("city", "id", "name"))
     model.setRelation(3, QSqlRelation("country", "id", "name"))
-    model.setHeaderData(0, Qt.Horizontal, QObject().tr("ID"))
+    model.setHeaderData(0, Qt.Orientation.Horizontal, QObject().tr("ID"))
 
-    model.setHeaderData(1, Qt.Horizontal, QObject().tr("Name"))
-    model.setHeaderData(2, Qt.Horizontal, QObject().tr("City"))
-    model.setHeaderData(3, Qt.Horizontal, QObject().tr("Country"))
+    model.setHeaderData(1, Qt.Orientation.Horizontal, QObject().tr("Name"))
+    model.setHeaderData(2, Qt.Orientation.Horizontal, QObject().tr("City"))
+    model.setHeaderData(3, Qt.Orientation.Horizontal, QObject().tr("Country"))
 
     model.select()
 
index 0d52c5b00465a636065390266b7a897470f40fcb..146fd739e0a8b67b1196e44b1931ad41d2b012a2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the examples/statemachine/moveblocks example from Qt v6.x"""
 
@@ -34,7 +35,7 @@ class QGraphicsRectWidget(QGraphicsWidget):
 
     def paint(self, painter: QPainter,
               option: QStyleOptionGraphicsItem, widget: QWidget | None = None):
-        painter.fillRect(self.rect(), Qt.blue)
+        painter.fillRect(self.rect(), Qt.GlobalColor.blue)
 
 
 class StateSwitchTransition(QAbstractTransition):
@@ -109,7 +110,7 @@ if __name__ == '__main__':
     button4.setZValue(3)
 
     scene = QGraphicsScene(0, 0, 300, 300)
-    scene.setBackgroundBrush(Qt.black)
+    scene.setBackgroundBrush(Qt.GlobalColor.black)
     scene.addItem(button1)
     scene.addItem(button2)
     scene.addItem(button3)
@@ -117,9 +118,9 @@ if __name__ == '__main__':
 
     window = GraphicsView(scene)
     window.setFrameStyle(0)
-    window.setAlignment(Qt.AlignLeft | Qt.AlignTop)
-    window.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
-    window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+    window.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
+    window.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+    window.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
 
     machine = QStateMachine()
 
@@ -177,28 +178,28 @@ if __name__ == '__main__':
 
     anim = QPropertyAnimation(button4, b"geometry")
     anim.setDuration(1000)
-    anim.setEasingCurve(QEasingCurve.OutElastic)
+    anim.setEasingCurve(QEasingCurve.Type.OutElastic)
     animation_group.addAnimation(anim)
 
     sub_group = QSequentialAnimationGroup(animation_group)
     sub_group.addPause(100)
     anim = QPropertyAnimation(button3, b"geometry")
     anim.setDuration(1000)
-    anim.setEasingCurve(QEasingCurve.OutElastic)
+    anim.setEasingCurve(QEasingCurve.Type.OutElastic)
     sub_group.addAnimation(anim)
 
     sub_group = QSequentialAnimationGroup(animation_group)
     sub_group.addPause(150)
     anim = QPropertyAnimation(button2, b"geometry")
     anim.setDuration(1000)
-    anim.setEasingCurve(QEasingCurve.OutElastic)
+    anim.setEasingCurve(QEasingCurve.Type.OutElastic)
     sub_group.addAnimation(anim)
 
     sub_group = QSequentialAnimationGroup(animation_group)
     sub_group.addPause(200)
     anim = QPropertyAnimation(button1, b"geometry")
     anim.setDuration(1000)
-    anim.setEasingCurve(QEasingCurve.OutElastic)
+    anim.setEasingCurve(QEasingCurve.Type.OutElastic)
     sub_group.addAnimation(anim)
 
     state_switcher = StateSwitcher(machine)
diff --git a/examples/statemachine/ping_pong/doc/ping_pong.rst b/examples/statemachine/ping_pong/doc/ping_pong.rst
new file mode 100644 (file)
index 0000000..020f9d9
--- /dev/null
@@ -0,0 +1,12 @@
+StateMachine Ping Pong Example
+==============================
+
+The Ping Pong States example shows how to use parallel states together
+with custom events and transitions in The State Machine Framework.
+It corresponds to the Qt example `Ping Pong States Example
+<https://doc.qt.io/qt-5/qtwidgets-statemachine-pingpong-example.html>`_.
+
+.. image:: pingpong.png
+    :align: center
+    :alt: ping_pong screenshot
+    :width: 400
diff --git a/examples/statemachine/ping_pong/doc/pingpong.png b/examples/statemachine/ping_pong/doc/pingpong.png
new file mode 100644 (file)
index 0000000..af707e4
Binary files /dev/null and b/examples/statemachine/ping_pong/doc/pingpong.png differ
index d5c18eb2859d6268cbe42df4ec0682c890fc60bc..e2ea9fd6712edc3faeab9978c8de50451f5ab4c0 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
diff --git a/examples/statemachine/rogue/doc/rogue.png b/examples/statemachine/rogue/doc/rogue.png
new file mode 100644 (file)
index 0000000..7aeb0e5
Binary files /dev/null and b/examples/statemachine/rogue/doc/rogue.png differ
diff --git a/examples/statemachine/rogue/doc/rogue.rst b/examples/statemachine/rogue/doc/rogue.rst
new file mode 100644 (file)
index 0000000..e8d2031
--- /dev/null
@@ -0,0 +1,11 @@
+StateMachine Rogue Example
+==========================
+
+The Rogue example shows how to use the Qt state machine for event handling
+It corresponds to the Qt example `Rogue Example
+<https://doc.qt.io/qt-5/qtwidgets-statemachine-rogue-example.html>`_.
+
+.. image:: rogue.png
+    :align: center
+    :alt: rogue screenshot
+    :width: 400
index f0ce9c28c2ced0c128c6f79c8b923ec29e039a29..20376b3c795d7a73cd2695543c3b14ecc47b7230 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -14,26 +15,26 @@ from PySide6.QtStateMachine import (QEventTransition, QFinalState,
 
 class MovementTransition(QEventTransition):
     def __init__(self, window):
-        super().__init__(window, QEvent.KeyPress)
+        super().__init__(window, QEvent.Type.KeyPress)
         self.window = window
 
     def eventTest(self, event):
-        if (event.type() == QEvent.StateMachineWrapped
-                and event.event().type() == QEvent.KeyPress):
+        if (event.type() == QEvent.Type.StateMachineWrapped
+                and event.event().type() == QEvent.Type.KeyPress):
             key = event.event().key()
-            return (key == Qt.Key_2 or key == Qt.Key_8
-                    or key == Qt.Key_6 or key == Qt.Key_4)
+            return (key == Qt.Key.Key_2 or key == Qt.Key.Key_8
+                    or key == Qt.Key.Key_6 or key == Qt.Key.Key_4)
         return False
 
     def onTransition(self, event):
         key = event.event().key()
-        if key == Qt.Key_4:
+        if key == Qt.Key.Key_4:
             self.window.move_player(self.window.left)
-        if key == Qt.Key_8:
+        if key == Qt.Key.Key_8:
             self.window.move_player(self.window.Up)
-        if key == Qt.Key_6:
+        if key == Qt.Key.Key_6:
             self.window.move_player(self.window.right)
-        if key == Qt.Key_2:
+        if key == Qt.Key.Key_2:
             self.window.move_player(self.window.down)
 
 
@@ -99,16 +100,16 @@ class MainWindow(QMainWindow):
         quit_state = QState(machine)
         quit_state.assignProperty(self, 'status', 'Really quit(y/n)?')
 
-        yes_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Y)
+        yes_transition = QKeyEventTransition(self, QEvent.Type.KeyPress, Qt.Key.Key_Y)
         self._final_state = QFinalState(machine)
         yes_transition.setTargetState(self._final_state)
         quit_state.addTransition(yes_transition)
 
-        no_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_N)
+        no_transition = QKeyEventTransition(self, QEvent.Type.KeyPress, Qt.Key.Key_N)
         no_transition.setTargetState(input_state)
         quit_state.addTransition(no_transition)
 
-        quit_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Q)
+        quit_transition = QKeyEventTransition(self, QEvent.Type.KeyPress, Qt.Key.Key_Q)
         quit_transition.setTargetState(quit_state)
         input_state.addTransition(quit_transition)
 
@@ -127,8 +128,8 @@ class MainWindow(QMainWindow):
             font_height = metrics.height()
             font_width = metrics.horizontalAdvance('X')
 
-            painter.fillRect(self.rect(), Qt.black)
-            painter.setPen(Qt.white)
+            painter.fillRect(self.rect(), Qt.GlobalColor.black)
+            painter.setPen(Qt.GlobalColor.white)
 
             y_pos = font_height
             painter.drawText(QPoint(0, y_pos), self.status)
index 1e58384f96e8490ef8dc0bdbac609aa5e1807742..c5c7f1da379c6b77cfd268a434fca341c0ba8626 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -37,7 +38,7 @@ class LightWidget(QWidget):
         if not self._on_val:
             return
         with QPainter(self) as painter:
-            painter.setRenderHint(QPainter.Antialiasing)
+            painter.setRenderHint(QPainter.RenderHint.Antialiasing)
             painter.setBrush(self.color)
             painter.drawEllipse(0, 0, self.width(), self.height())
 
@@ -48,14 +49,14 @@ class TrafficLightWidget(QWidget):
     def __init__(self):
         super().__init__()
         vbox = QVBoxLayout(self)
-        self._red_light = LightWidget(Qt.red)
+        self._red_light = LightWidget(Qt.GlobalColor.red)
         vbox.addWidget(self._red_light)
-        self._yellow_light = LightWidget(Qt.yellow)
+        self._yellow_light = LightWidget(Qt.GlobalColor.yellow)
         vbox.addWidget(self._yellow_light)
-        self._green_light = LightWidget(Qt.green)
+        self._green_light = LightWidget(Qt.GlobalColor.green)
         vbox.addWidget(self._green_light)
         pal = QPalette()
-        pal.setColor(QPalette.Window, Qt.black)
+        pal.setColor(QPalette.ColorRole.Window, Qt.GlobalColor.black)
         self.setPalette(pal)
         self.setAutoFillBackground(True)
 
diff --git a/examples/tutorials/finance_manager/part1/Finance/AddDialog.qml b/examples/tutorials/finance_manager/part1/Finance/AddDialog.qml
new file mode 100644 (file)
index 0000000..eea14f5
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Dialog {
+    id: dialog
+
+    signal finished(string itemName, string category, real cost, string date)
+
+    contentItem: ColumnLayout {
+        id: form
+        spacing: 10
+        property alias itemName: itemName
+        property alias category: category
+        property alias cost: cost
+        property alias date: date
+
+        GridLayout {
+            columns: 2
+            columnSpacing: 20
+            rowSpacing: 10
+            Layout.fillWidth: true
+
+            Label {
+                text: qsTr("Item Name:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: itemName
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            Label {
+                text: qsTr("Category:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: category
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            Label {
+                text: qsTr("Cost:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: cost
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+                placeholderText: qsTr("€")
+                inputMethodHints: Qt.ImhFormattedNumbersOnly
+            }
+
+            Label {
+                text: qsTr("Date:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: date
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+                // placeholderText: qsTr("dd-mm-yyyy")
+                validator: RegularExpressionValidator { regularExpression: /^[0-3]?\d-[01]?\d-\d{4}$/ }
+                // code to add the - automatically
+                onTextChanged: {
+                    if (date.text.length === 2 || date.text.length === 5) {
+                        date.text += "-"
+                    }
+                }
+                Component.onCompleted: {
+                var today = new Date();
+                var day = String(today.getDate()).padStart(2, '0');
+                var month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+                var year = today.getFullYear();
+                date.placeholderText = day + "-" + month + "-" + year;
+                }
+            }
+        }
+    }
+
+    function createEntry() {
+        form.itemName.clear()
+        form.category.clear()
+        form.cost.clear()
+        form.date.clear()
+        dialog.title = qsTr("Add Finance Item")
+        dialog.open()
+    }
+
+    x: parent.width / 2 - width / 2
+    y: parent.height / 2 - height / 2
+
+    focus: true
+    modal: true
+    title: qsTr("Add Finance Item")
+    standardButtons: Dialog.Ok | Dialog.Cancel
+
+    Component.onCompleted: {
+        dialog.visible = false
+        Qt.inputMethod.visibleChanged.connect(adjustDialogPosition)
+    }
+
+    function adjustDialogPosition() {
+        if (Qt.inputMethod.visible) {
+            // If the keyboard is visible, move the dialog up
+            dialog.y = parent.height / 4 - height / 2
+        } else {
+            // If the keyboard is not visible, center the dialog
+            dialog.y = parent.height / 2 - height / 2
+        }
+    }
+
+    onAccepted: {
+        finished(form.itemName.text, form.category.text, parseFloat(form.cost.text), form.date.text)
+    }
+}
diff --git a/examples/tutorials/finance_manager/part1/Finance/FinanceDelegate.qml b/examples/tutorials/finance_manager/part1/Finance/FinanceDelegate.qml
new file mode 100644 (file)
index 0000000..abea906
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ItemDelegate {
+    id: delegate
+    checkable: true
+    width: parent.width
+    height: Qt.platform.os == "android" ?
+        Math.min(window.width, window.height) * 0.15 :
+        Math.min(window.width, window.height) * 0.1
+
+    contentItem:
+    RowLayout {
+        Label {
+            id: dateLabel
+            font.pixelSize: Qt.platform.os == "android" ?
+                Math.min(window.width, window.height) * 0.03 :
+                Math.min(window.width, window.height) * 0.02
+            text: date
+            elide: Text.ElideRight
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+            color: Material.primaryTextColor
+        }
+
+        ColumnLayout {
+            spacing: 5
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+
+            Label {
+                text: item_name
+                color: "#5c8540"
+                font.bold: true
+                elide: Text.ElideRight
+                font.pixelSize:  Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+                Layout.fillWidth: true
+            }
+
+            Label {
+                text: category
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize:  Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+        }
+
+        Item {
+        Layout.fillWidth: true  // This item will take up the remaining space
+        }
+
+        ColumnLayout {
+            spacing: 5
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+
+            Label {
+                text: "you spent:"
+                color: "#5c8540"
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize:  Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+
+            Label {
+                text: cost + "€"
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize:  Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part1/Finance/FinancePieChart.qml b/examples/tutorials/finance_manager/part1/Finance/FinancePieChart.qml
new file mode 100644 (file)
index 0000000..d05a49c
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+import QtGraphs
+import QtQuick.Controls.Material
+
+Item {
+    width: Screen.width
+    height: Screen.height
+
+    GraphsView {
+        id: chart
+        anchors.fill: parent
+        antialiasing: true
+
+        theme: GraphsTheme {
+            colorScheme: Qt.Dark
+            theme: GraphsTheme.Theme.QtGreenNeon
+        }
+
+        PieSeries {
+            id: pieSeries
+        }
+    }
+
+    Text {
+        id: chartTitle
+        text: "Total Expenses Breakdown by Category"
+        color: "#5c8540"
+        font.pixelSize: Qt.platform.os == "android" ?
+            Math.min(window.width, window.height) * 0.04 :
+            Math.min(window.width, window.height) * 0.03
+        anchors.horizontalCenter: parent.horizontalCenter
+        anchors.top: parent.top
+        anchors.topMargin: 20
+    }
+
+    function updateChart(data) {
+        pieSeries.clear()
+        for (var category in data) {
+            var slice = pieSeries.append(category, data[category])
+            slice.label = category + ": " + data[category] + "€"
+            slice.labelVisible = true
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part1/Finance/FinanceView.qml b/examples/tutorials/finance_manager/part1/Finance/FinanceView.qml
new file mode 100644 (file)
index 0000000..44d6c20
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ListView {
+    id: listView
+    anchors.fill: parent
+    height: parent.height
+    property var financeModel
+
+    delegate: FinanceDelegate {
+        id: delegate
+        width: listView.width
+    }
+
+    model: financeModel
+
+    section.property: "month"  // Group items by the "month" property
+    section.criteria: ViewSection.FullString
+    section.delegate: Component {
+        id: sectionHeading
+        Rectangle {
+            width: listView.width
+            height:  Qt.platform.os == "android" ?
+                Math.min(window.width, window.height) * 0.05 :
+                Math.min(window.width, window.height) * 0.03
+            color: "#5c8540"
+
+            required property string section
+
+            Text {
+                text: parent.section
+                font.bold: true
+                // depending on the screen density, adjust the font size
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+                color: Material.primaryTextColor
+            }
+        }
+    }
+
+    ScrollBar.vertical: ScrollBar { }
+}
diff --git a/examples/tutorials/finance_manager/part1/Finance/Main.qml b/examples/tutorials/finance_manager/part1/Finance/Main.qml
new file mode 100644 (file)
index 0000000..b06b3f5
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+import Finance
+
+ApplicationWindow {
+    id: window
+    Material.theme: Material.Dark
+    Material.accent: Material.Gray
+    width: Screen.width * 0.3
+    height: Screen.height * 0.5
+    visible: true
+    title: qsTr("Finance Manager")
+
+    // Add a toolbar for the application, only visible on mobile
+    header: ToolBar {
+        Material.primary: "#5c8540"
+        visible: Qt.platform.os == "android"
+        RowLayout {
+            anchors.fill: parent
+            Label {
+                text: qsTr("Finance Manager")
+                font.pixelSize: 20
+                Layout.alignment: Qt.AlignCenter
+            }
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        TabBar {
+            id: tabBar
+            Layout.fillWidth: true
+
+            TabButton {
+                text: qsTr("Expenses")
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                onClicked: stackView.currentIndex = 0
+            }
+
+            TabButton {
+                text: qsTr("Charts")
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                onClicked: stackView.currentIndex = 1
+            }
+        }
+
+        StackLayout {
+            id: stackView
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            Item {
+                id: expensesView
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+
+                FinanceView {
+                    id: financeView
+                    anchors.fill: parent
+                    financeModel: finance_model
+                }
+            }
+
+            Item {
+                id: chartsView
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+
+                FinancePieChart {
+                    id: financePieChart
+                    anchors.fill: parent
+                    Component.onCompleted: {
+                        var categoryData = finance_model.getCategoryData()
+                        updateChart(categoryData)
+                    }
+                }
+            }
+        }
+    }
+
+    // Model to store the finance data. Created from Python.
+    FinanceModel {
+        id: finance_model
+    }
+
+    // Add a dialog to add new entries
+    AddDialog {
+        id: addDialog
+        onFinished: function(item_name, category, cost, date) {
+            finance_model.append(item_name, category, cost, date)
+            var categoryData = finance_model.getCategoryData()
+            financePieChart.updateChart(categoryData)
+        }
+    }
+
+    // Add a button to open the dialog
+    ToolButton {
+        id: roundButton
+        text: qsTr("+")
+        highlighted: true
+        Material.elevation: 6
+        width: Qt.platform.os === "android" ?
+            Math.min(parent.width * 0.2, Screen.width * 0.15) :
+            Math.min(parent.width * 0.060, Screen.width * 0.05)
+        height: width  // Keep the button circular
+        anchors.margins: 10
+        anchors.right: parent.right
+        anchors.bottom: parent.bottom
+        background: Rectangle {
+            color: "#5c8540"
+            radius: roundButton.width / 2
+        }
+        font.pixelSize: width * 0.4
+        onClicked: {
+            addDialog.createEntry()
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part1/Finance/qmldir b/examples/tutorials/finance_manager/part1/Finance/qmldir
new file mode 100644 (file)
index 0000000..5362013
--- /dev/null
@@ -0,0 +1,6 @@
+module Finance
+Main 1.0 Main.qml
+FinanceView 1.0 FinanceView.qml
+FinancePieChart 1.0 FinancePieChart.qml
+FinanceDelegate 1.0 FinanceDelegate.qml
+AddDialog 1.0 AddDialog.qml
diff --git a/examples/tutorials/finance_manager/part1/doc/part1.md b/examples/tutorials/finance_manager/part1/doc/part1.md
new file mode 100644 (file)
index 0000000..0c8d9fa
--- /dev/null
@@ -0,0 +1,11 @@
+# Finance Manager Example - Part 1
+
+This example represents the part one of the tutorial series on creating a simple Finance Manager
+that allows users to manage their expenses and visualize them using a pie chart, using PySide6,
+[SQLAlchemy], [FastAPI], and [Pydantic].
+
+For more details, see the [Finance Manager Tutorial - Part 1](tutorial_financemanager_partone).
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
diff --git a/examples/tutorials/finance_manager/part1/financemodel.py b/examples/tutorials/finance_manager/part1/financemodel.py
new file mode 100644 (file)
index 0000000..9ab60fe
--- /dev/null
@@ -0,0 +1,94 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from datetime import datetime
+from dataclasses import dataclass
+from enum import IntEnum
+from collections import defaultdict
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+                            QByteArray)
+from PySide6.QtQml import QmlElement
+
+QML_IMPORT_NAME = "Finance"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class FinanceModel(QAbstractListModel):
+
+    @QEnum
+    class FinanceRole(IntEnum):
+        ItemNameRole = Qt.ItemDataRole.DisplayRole
+        CategoryRole = Qt.ItemDataRole.UserRole
+        CostRole = Qt.ItemDataRole.UserRole + 1
+        DateRole = Qt.ItemDataRole.UserRole + 2
+        MonthRole = Qt.ItemDataRole.UserRole + 3
+
+    @dataclass
+    class Finance:
+        item_name: str
+        category: str
+        cost: float
+        date: str
+
+        @property
+        def month(self):
+            return datetime.strptime(self.date, "%d-%m-%Y").strftime("%B %Y")
+
+    def __init__(self, parent=None) -> None:
+        super().__init__(parent)
+        self.m_finances = []
+        self.m_finances.append(self.Finance("Mobile Prepaid", "Electronics", 20.00, "15-02-2024"))
+        self.m_finances.append(self.Finance("Groceries-Feb-Week1", "Groceries", 60.75,
+                                            "16-01-2024"))
+        self.m_finances.append(self.Finance("Bus Ticket", "Transport", 5.50, "17-01-2024"))
+        self.m_finances.append(self.Finance("Book", "Education", 25.00, "18-01-2024"))
+
+    def rowCount(self, parent=QModelIndex()):
+        return len(self.m_finances)
+
+    def data(self, index: QModelIndex, role: int):
+        row = index.row()
+        if row < self.rowCount():
+            finance = self.m_finances[row]
+            if role == FinanceModel.FinanceRole.ItemNameRole:
+                return finance.item_name
+            if role == FinanceModel.FinanceRole.CategoryRole:
+                return finance.category
+            if role == FinanceModel.FinanceRole.CostRole:
+                return finance.cost
+            if role == FinanceModel.FinanceRole.DateRole:
+                return finance.date
+            if role == FinanceModel.FinanceRole.MonthRole:
+                return finance.month
+        return None
+
+    @Slot(result=dict)
+    def getCategoryData(self):
+        category_data = defaultdict(float)
+        for finance in self.m_finances:
+            category_data[finance.category] += finance.cost
+        return dict(category_data)
+
+    def roleNames(self):
+        roles = super().roleNames()
+        roles[FinanceModel.FinanceRole.ItemNameRole] = QByteArray(b"item_name")
+        roles[FinanceModel.FinanceRole.CategoryRole] = QByteArray(b"category")
+        roles[FinanceModel.FinanceRole.CostRole] = QByteArray(b"cost")
+        roles[FinanceModel.FinanceRole.DateRole] = QByteArray(b"date")
+        roles[FinanceModel.FinanceRole.MonthRole] = QByteArray(b"month")
+        return roles
+
+    @Slot(int, result='QVariantMap')
+    def get(self, row: int):
+        finance = self.m_finances[row]
+        return {"item_name": finance.item_name, "category": finance.category,
+                "cost": finance.cost, "date": finance.date}
+
+    @Slot(str, str, float, str)
+    def append(self, item_name: str, category: str, cost: float, date: str):
+        finance = self.Finance(item_name, category, cost, date)
+        self.beginInsertRows(QModelIndex(), 0, 0)  # Insert at the front
+        self.m_finances.insert(0, finance)  # Insert at the front of the list
+        self.endInsertRows()
diff --git a/examples/tutorials/finance_manager/part1/main.py b/examples/tutorials/finance_manager/part1/main.py
new file mode 100644 (file)
index 0000000..7a794db
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+from PySide6.QtWidgets import QApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from financemodel import FinanceModel  # noqa: F401
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    QApplication.setOrganizationName("QtProject")
+    QApplication.setApplicationName("Finance Manager")
+    engine = QQmlApplicationEngine()
+
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("Finance", "Main")
+
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
diff --git a/examples/tutorials/finance_manager/part1/part1.pyproject b/examples/tutorials/finance_manager/part1/part1.pyproject
new file mode 100644 (file)
index 0000000..e02b93b
--- /dev/null
@@ -0,0 +1,12 @@
+{
+    "files": [
+        "main.py",
+        "financemodel.py",
+        "Finance/AddDialog.qml",
+        "Finance/FinanceDelegate.qml",
+        "Finance/FinancePieChart.qml",
+        "Finance/FinanceView.qml",
+        "Finance/Main.qml",
+        "Finance/qmldir"
+    ]
+}
diff --git a/examples/tutorials/finance_manager/part2/Finance/AddDialog.qml b/examples/tutorials/finance_manager/part2/Finance/AddDialog.qml
new file mode 100644 (file)
index 0000000..b61c7f9
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Dialog {
+    id: dialog
+
+    signal finished(string itemName, string category, real cost, string date)
+
+    contentItem: ColumnLayout {
+        id: form
+        spacing: 10
+        property alias itemName: itemName
+        property alias category: category
+        property alias cost: cost
+        property alias date: date
+
+        GridLayout {
+            columns: 2
+            columnSpacing: 20
+            rowSpacing: 10
+            Layout.fillWidth: true
+
+            Label {
+                text: qsTr("Item Name:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: itemName
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            Label {
+                text: qsTr("Category:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: category
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            Label {
+                text: qsTr("Cost:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: cost
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+                placeholderText: qsTr("€")
+                inputMethodHints: Qt.ImhFormattedNumbersOnly
+            }
+
+            Label {
+                text: qsTr("Date:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: date
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+                placeholderText: qsTr("dd-mm-yyyy")
+                validator: RegularExpressionValidator { regularExpression: /^[0-3]?\d-[01]?\d-\d{4}$/ }
+                // code to add the - automatically
+                onTextChanged: {
+                    if (date.text.length === 2 || date.text.length === 5) {
+                        date.text += "-"
+                    }
+                }
+                Component.onCompleted: {
+                var today = new Date();
+                var day = String(today.getDate()).padStart(2, '0');
+                var month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+                var year = today.getFullYear();
+                date.placeholderText = day + "-" + month + "-" + year;
+                }
+            }
+        }
+    }
+
+    function createEntry() {
+        form.itemName.clear()
+        form.category.clear()
+        form.cost.clear()
+        form.date.clear()
+        dialog.title = qsTr("Add Finance Item")
+        dialog.open()
+    }
+
+    x: parent.width / 2 - width / 2
+    y: parent.height / 2 - height / 2
+
+    focus: true
+    modal: true
+    title: qsTr("Add Finance Item")
+    standardButtons: Dialog.Ok | Dialog.Cancel
+
+    Component.onCompleted: {
+        dialog.visible = false
+        Qt.inputMethod.visibleChanged.connect(adjustDialogPosition)
+    }
+
+    function adjustDialogPosition() {
+        if (Qt.inputMethod.visible) {
+            // If the keyboard is visible, move the dialog up
+            dialog.y = parent.height / 4 - height / 2
+        } else {
+            // If the keyboard is not visible, center the dialog
+            dialog.y = parent.height / 2 - height / 2
+        }
+    }
+
+    onAccepted: {
+        finished(form.itemName.text, form.category.text, parseFloat(form.cost.text), form.date.text)
+    }
+}
diff --git a/examples/tutorials/finance_manager/part2/Finance/FinanceDelegate.qml b/examples/tutorials/finance_manager/part2/Finance/FinanceDelegate.qml
new file mode 100644 (file)
index 0000000..267d57d
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ItemDelegate {
+    id: delegate
+    checkable: true
+    width: parent.width
+    height: Qt.platform.os == "android" ?
+        Math.min(window.width, window.height) * 0.15 :
+        Math.min(window.width, window.height) * 0.1
+
+    contentItem:
+    RowLayout {
+        Label {
+            id: dateLabel
+            font.pixelSize: Qt.platform.os == "android" ?
+                Math.min(window.width, window.height) * 0.03 :
+                Math.min(window.width, window.height) * 0.02
+            text: date
+            elide: Text.ElideRight
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+            color: Material.primaryTextColor
+        }
+
+        ColumnLayout {
+            spacing: 5
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+
+            Label {
+                text: item_name
+                color: "#5c8540"
+                font.bold: true
+                elide: Text.ElideRight
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                Layout.fillWidth: true
+            }
+
+            Label {
+                text: category
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+        }
+
+        Item {
+        Layout.fillWidth: true  // This item will take up the remaining space
+        }
+
+        ColumnLayout {
+            spacing: 5
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+
+            Label {
+                text: "you spent:"
+                color: "#5c8540"
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+
+            Label {
+                text: cost + "€"
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part2/Finance/FinancePieChart.qml b/examples/tutorials/finance_manager/part2/Finance/FinancePieChart.qml
new file mode 100644 (file)
index 0000000..f538632
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+import QtQuick.Controls.Material
+
+Item {
+    width: Screen.width
+    height: Screen.height
+
+    GraphsView {
+        id: chart
+        anchors.fill: parent
+        antialiasing: true
+
+        theme: GraphsTheme {
+            colorScheme: Qt.Dark
+            theme: GraphsTheme.Theme.QtGreenNeon
+        }
+
+        PieSeries {
+            id: pieSeries
+        }
+    }
+
+    Text {
+        id: chartTitle
+        text: "Total Expenses Breakdown by Category"
+        color: "#5c8540"
+        font.pixelSize: Qt.platform.os == "android" ?
+            Math.min(window.width, window.height) * 0.04 :
+            Math.min(window.width, window.height) * 0.03
+        anchors.horizontalCenter: parent.horizontalCenter
+        anchors.top: parent.top
+        anchors.topMargin: 20
+    }
+
+    function updateChart(data) {
+        pieSeries.clear()
+        for (var category in data) {
+            var slice = pieSeries.append(category, data[category])
+            slice.label = category + ": " + data[category] + "€"
+            slice.labelVisible = true
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part2/Finance/FinanceView.qml b/examples/tutorials/finance_manager/part2/Finance/FinanceView.qml
new file mode 100644 (file)
index 0000000..8a943e0
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ListView {
+    id: listView
+    anchors.fill: parent
+    height: parent.height
+    property var financeModel
+
+    delegate: FinanceDelegate {
+        id: delegate
+        width: listView.width
+    }
+
+    model: financeModel
+
+    section.property: "month"  // Group items by the "month" property
+    section.criteria: ViewSection.FullString
+    section.delegate: Component {
+        id: sectionHeading
+        Rectangle {
+            width: listView.width
+            height:  Qt.platform.os == "android" ?
+                Math.min(window.width, window.height) * 0.05 :
+                Math.min(window.width, window.height) * 0.03
+            color: "#5c8540"
+
+            required property string section
+
+            Text {
+                text: parent.section
+                font.bold: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+                color: Material.primaryTextColor
+            }
+        }
+    }
+
+    ScrollBar.vertical: ScrollBar { }
+}
diff --git a/examples/tutorials/finance_manager/part2/Finance/Main.qml b/examples/tutorials/finance_manager/part2/Finance/Main.qml
new file mode 100644 (file)
index 0000000..b06b3f5
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+import Finance
+
+ApplicationWindow {
+    id: window
+    Material.theme: Material.Dark
+    Material.accent: Material.Gray
+    width: Screen.width * 0.3
+    height: Screen.height * 0.5
+    visible: true
+    title: qsTr("Finance Manager")
+
+    // Add a toolbar for the application, only visible on mobile
+    header: ToolBar {
+        Material.primary: "#5c8540"
+        visible: Qt.platform.os == "android"
+        RowLayout {
+            anchors.fill: parent
+            Label {
+                text: qsTr("Finance Manager")
+                font.pixelSize: 20
+                Layout.alignment: Qt.AlignCenter
+            }
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        TabBar {
+            id: tabBar
+            Layout.fillWidth: true
+
+            TabButton {
+                text: qsTr("Expenses")
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                onClicked: stackView.currentIndex = 0
+            }
+
+            TabButton {
+                text: qsTr("Charts")
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                onClicked: stackView.currentIndex = 1
+            }
+        }
+
+        StackLayout {
+            id: stackView
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            Item {
+                id: expensesView
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+
+                FinanceView {
+                    id: financeView
+                    anchors.fill: parent
+                    financeModel: finance_model
+                }
+            }
+
+            Item {
+                id: chartsView
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+
+                FinancePieChart {
+                    id: financePieChart
+                    anchors.fill: parent
+                    Component.onCompleted: {
+                        var categoryData = finance_model.getCategoryData()
+                        updateChart(categoryData)
+                    }
+                }
+            }
+        }
+    }
+
+    // Model to store the finance data. Created from Python.
+    FinanceModel {
+        id: finance_model
+    }
+
+    // Add a dialog to add new entries
+    AddDialog {
+        id: addDialog
+        onFinished: function(item_name, category, cost, date) {
+            finance_model.append(item_name, category, cost, date)
+            var categoryData = finance_model.getCategoryData()
+            financePieChart.updateChart(categoryData)
+        }
+    }
+
+    // Add a button to open the dialog
+    ToolButton {
+        id: roundButton
+        text: qsTr("+")
+        highlighted: true
+        Material.elevation: 6
+        width: Qt.platform.os === "android" ?
+            Math.min(parent.width * 0.2, Screen.width * 0.15) :
+            Math.min(parent.width * 0.060, Screen.width * 0.05)
+        height: width  // Keep the button circular
+        anchors.margins: 10
+        anchors.right: parent.right
+        anchors.bottom: parent.bottom
+        background: Rectangle {
+            color: "#5c8540"
+            radius: roundButton.width / 2
+        }
+        font.pixelSize: width * 0.4
+        onClicked: {
+            addDialog.createEntry()
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part2/Finance/qmldir b/examples/tutorials/finance_manager/part2/Finance/qmldir
new file mode 100644 (file)
index 0000000..5362013
--- /dev/null
@@ -0,0 +1,6 @@
+module Finance
+Main 1.0 Main.qml
+FinanceView 1.0 FinanceView.qml
+FinancePieChart 1.0 FinancePieChart.qml
+FinanceDelegate 1.0 FinanceDelegate.qml
+AddDialog 1.0 AddDialog.qml
diff --git a/examples/tutorials/finance_manager/part2/database.py b/examples/tutorials/finance_manager/part2/database.py
new file mode 100644 (file)
index 0000000..08cbb62
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from sqlalchemy import create_engine, Column, Integer, String, Float
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+import os
+import platform
+from pathlib import Path
+
+Base = declarative_base()
+
+
+class Finance(Base):
+    __tablename__ = 'finances'
+    id = Column(Integer, primary_key=True)
+    item_name = Column(String)
+    category = Column(String)
+    cost = Column(Float)
+    date = Column(String)
+
+
+# Check for an environment variable for the database path
+env_db_path = os.getenv('FINANCE_MANAGER_DB_PATH')
+
+if env_db_path:
+    db_path = Path(env_db_path)
+else:
+    # Determine the application data directory based on the operating system using pathlib
+    if platform.system() == 'Windows':
+        app_data_location = Path(os.getenv('APPDATA')) / 'FinanceManager'
+    elif platform.system() == 'Darwin':  # macOS
+        app_data_location = Path.home() / 'Library' / 'Application Support' / 'FinanceManager'
+    else:  # Linux and other Unix-like systems
+        app_data_location = Path.home() / '.local' / 'share' / 'FinanceManager'
+
+    db_path = app_data_location / 'finances.db'
+
+DATABASE_URL = f'sqlite:///{db_path}'
+engine = create_engine(DATABASE_URL)
+Session = sessionmaker(bind=engine)
+
+# Default data to be added to the database
+default_data = [
+    {"item_name": "Mobile Prepaid", "category": "Electronics", "cost": 20.00, "date": "15-02-2024"},
+    {"item_name": "Groceries-Feb-Week1", "category": "Groceries", "cost": 60.75,
+     "date": "16-01-2024"},
+    {"item_name": "Bus Ticket", "category": "Transport", "cost": 5.50, "date": "17-01-2024"},
+    {"item_name": "Book", "category": "Education", "cost": 25.00, "date": "18-01-2024"},
+]
+
+
+def initialize_database():
+    if db_path.exists():
+        print(f"Database '{db_path}' already exists.")
+        return
+
+    app_data_location.mkdir(parents=True, exist_ok=True)
+    Base.metadata.create_all(engine)
+    print(f"Database '{db_path}' created successfully.")
+    session = Session()
+
+    for data in default_data:
+        finance = Finance(**data)
+        session.add(finance)
+
+    session.commit()
+    print("Default data has been added to the database.")
diff --git a/examples/tutorials/finance_manager/part2/doc/part2.md b/examples/tutorials/finance_manager/part2/doc/part2.md
new file mode 100644 (file)
index 0000000..1454d23
--- /dev/null
@@ -0,0 +1,11 @@
+# Finance Manager Example - Part 2
+
+This example represents the part two of the tutorial series on creating a simple Finance Manager
+that allows users to manage their expenses and visualize them using a pie chart, using PySide6,
+[SQLAlchemy], [FastAPI], and [Pydantic].
+
+For more details, see the [Finance Manager Tutorial - Part 2](tutorial_financemanager_parttwo).
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
diff --git a/examples/tutorials/finance_manager/part2/financemodel.py b/examples/tutorials/finance_manager/part2/financemodel.py
new file mode 100644 (file)
index 0000000..7e48c2a
--- /dev/null
@@ -0,0 +1,101 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from datetime import datetime
+from dataclasses import dataclass
+from enum import IntEnum
+from collections import defaultdict
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+                            QByteArray)
+from PySide6.QtQml import QmlElement
+import database
+
+QML_IMPORT_NAME = "Finance"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class FinanceModel(QAbstractListModel):
+
+    @QEnum
+    class FinanceRole(IntEnum):
+        ItemNameRole = Qt.ItemDataRole.DisplayRole
+        CategoryRole = Qt.ItemDataRole.UserRole
+        CostRole = Qt.ItemDataRole.UserRole + 1
+        DateRole = Qt.ItemDataRole.UserRole + 2
+        MonthRole = Qt.ItemDataRole.UserRole + 3
+
+    @dataclass
+    class Finance:
+        item_name: str
+        category: str
+        cost: float
+        date: str
+
+        @property
+        def month(self):
+            return datetime.strptime(self.date, "%d-%m-%Y").strftime("%B %Y")
+
+    def __init__(self, parent=None) -> None:
+        super().__init__(parent)
+        self.session = database.Session()
+        self.m_finances = self.load_finances()
+
+    def load_finances(self):
+        finances = []
+        for finance in self.session.query(database.Finance).all():
+            finances.append(self.Finance(finance.item_name, finance.category, finance.cost,
+                                         finance.date))
+        return finances
+
+    def rowCount(self, parent=QModelIndex()):
+        return len(self.m_finances)
+
+    def data(self, index: QModelIndex, role: int):
+        row = index.row()
+        if row < self.rowCount():
+            finance = self.m_finances[row]
+            if role == FinanceModel.FinanceRole.ItemNameRole:
+                return finance.item_name
+            if role == FinanceModel.FinanceRole.CategoryRole:
+                return finance.category
+            if role == FinanceModel.FinanceRole.CostRole:
+                return finance.cost
+            if role == FinanceModel.FinanceRole.DateRole:
+                return finance.date
+            if role == FinanceModel.FinanceRole.MonthRole:
+                return finance.month
+        return None
+
+    @Slot(result=dict)
+    def getCategoryData(self):
+        category_data = defaultdict(float)
+        for finance in self.m_finances:
+            category_data[finance.category] += finance.cost
+        return dict(category_data)
+
+    def roleNames(self):
+        roles = super().roleNames()
+        roles[FinanceModel.FinanceRole.ItemNameRole] = QByteArray(b"item_name")
+        roles[FinanceModel.FinanceRole.CategoryRole] = QByteArray(b"category")
+        roles[FinanceModel.FinanceRole.CostRole] = QByteArray(b"cost")
+        roles[FinanceModel.FinanceRole.DateRole] = QByteArray(b"date")
+        roles[FinanceModel.FinanceRole.MonthRole] = QByteArray(b"month")
+        return roles
+
+    @Slot(int, result='QVariantMap')
+    def get(self, row: int):
+        finance = self.m_finances[row]
+        return {"item_name": finance.item_name, "category": finance.category,
+                "cost": finance.cost, "date": finance.date}
+
+    @Slot(str, str, float, str)
+    def append(self, item_name: str, category: str, cost: float, date: str):
+        finance = self.Finance(item_name, category, cost, date)
+        self.session.add(database.Finance(item_name=item_name, category=category, cost=cost,
+                                          date=date))
+        self.beginInsertRows(QModelIndex(), 0, 0)  # Insert at the front
+        self.m_finances.insert(0, finance)  # Insert at the front of the list
+        self.endInsertRows()
+        self.session.commit()
diff --git a/examples/tutorials/finance_manager/part2/main.py b/examples/tutorials/finance_manager/part2/main.py
new file mode 100644 (file)
index 0000000..bff2976
--- /dev/null
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtWidgets import QApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from financemodel import FinanceModel  # noqa: F401
+from database import initialize_database
+
+if __name__ == '__main__':
+    # Initialize the database if it does not exist
+    initialize_database()
+
+    app = QApplication(sys.argv)
+    QApplication.setOrganizationName("QtProject")
+    QApplication.setApplicationName("Finance Manager")
+    engine = QQmlApplicationEngine()
+
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("Finance", "Main")
+
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
diff --git a/examples/tutorials/finance_manager/part2/part2.pyproject b/examples/tutorials/finance_manager/part2/part2.pyproject
new file mode 100644 (file)
index 0000000..74b88ad
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    "files": [
+        "database.py",
+        "main.py",
+        "financemodel.py",
+        "Finance/AddDialog.qml",
+        "Finance/FinanceDelegate.qml",
+        "Finance/FinancePieChart.qml",
+        "Finance/FinanceView.qml",
+        "Finance/Main.qml",
+        "Finance/qmldir",
+        "requirements.txt"
+    ]
+}
diff --git a/examples/tutorials/finance_manager/part2/requirements.txt b/examples/tutorials/finance_manager/part2/requirements.txt
new file mode 100644 (file)
index 0000000..39fb2be
--- /dev/null
@@ -0,0 +1 @@
+sqlalchemy
diff --git a/examples/tutorials/finance_manager/part3/Backend/database.py b/examples/tutorials/finance_manager/part3/Backend/database.py
new file mode 100644 (file)
index 0000000..39bcd88
--- /dev/null
@@ -0,0 +1,62 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from sqlalchemy import create_engine, Column, Integer, String, Float
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+import os
+import platform
+from pathlib import Path
+
+Base = declarative_base()
+
+
+class Finance(Base):
+    __tablename__ = 'finances'
+    id = Column(Integer, primary_key=True)
+    item_name = Column(String)
+    category = Column(String)
+    cost = Column(Float)
+    date = Column(String)
+
+
+# Determine the application data directory based on the operating system using pathlib
+if platform.system() == 'Windows':
+    app_data_location = Path(os.getenv('APPDATA')) / 'FinanceManager'
+elif platform.system() == 'Darwin':  # macOS
+    app_data_location = Path.home() / 'Library' / 'Application Support' / 'FinanceManager'
+else:  # Linux and other Unix-like systems
+    app_data_location = Path.home() / '.local' / 'share' / 'FinanceManager'
+
+db_path = app_data_location / 'finances.db'
+
+DATABASE_URL = f'sqlite:///{db_path}'
+engine = create_engine(DATABASE_URL)
+Session = sessionmaker(bind=engine)
+
+# Default data to be added to the database
+default_data = [
+    {"item_name": "Mobile Prepaid", "category": "Electronics", "cost": 20.00, "date": "15-02-2024"},
+    {"item_name": "Groceries-Feb-Week1", "category": "Groceries", "cost": 60.75,
+     "date": "16-01-2024"},
+    {"item_name": "Bus Ticket", "category": "Transport", "cost": 5.50, "date": "17-01-2024"},
+    {"item_name": "Book", "category": "Education", "cost": 25.00, "date": "18-01-2024"},
+]
+
+
+def initialize_database():
+    if db_path.exists():
+        print(f"Database '{db_path}' already exists.")
+        return
+
+    app_data_location.mkdir(parents=True, exist_ok=True)
+    Base.metadata.create_all(engine)
+    print(f"Database '{db_path}' created successfully.")
+    session = Session()
+
+    for data in default_data:
+        finance = Finance(**data)
+        session.add(finance)
+
+    session.commit()
+    print("Default data has been added to the database.")
diff --git a/examples/tutorials/finance_manager/part3/Backend/main.py b/examples/tutorials/finance_manager/part3/Backend/main.py
new file mode 100644 (file)
index 0000000..1ab0bd1
--- /dev/null
@@ -0,0 +1,16 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import uvicorn
+from database import initialize_database
+
+
+def main():
+    # Initialize the database
+    initialize_database()
+    # Start the FastAPI endpoint
+    uvicorn.run("rest_api:app", host="127.0.0.1", port=8000, reload=True)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/examples/tutorials/finance_manager/part3/Backend/rest_api.py b/examples/tutorials/finance_manager/part3/Backend/rest_api.py
new file mode 100644 (file)
index 0000000..b2a25f8
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import logging
+from fastapi import FastAPI, Depends, HTTPException
+from pydantic import BaseModel
+from typing import Dict, Any
+from sqlalchemy import orm
+from database import Session, Finance
+
+app = FastAPI()
+
+
+class FinanceCreate(BaseModel):
+    item_name: str
+    category: str
+    cost: float
+    date: str
+
+
+class FinanceRead(FinanceCreate):
+    class Config:
+        from_attributes = True
+
+
+def get_db():
+    db = Session()
+    try:
+        yield db
+    finally:
+        db.close()
+
+
+@app.post("/finances/", response_model=FinanceRead)
+def create_finance(finance: FinanceCreate, db: orm.Session = Depends(get_db)):
+    print(f"Adding finance item: {finance}")
+    db_finance = Finance(**finance.model_dump())
+    db.add(db_finance)
+    db.commit()
+    db.refresh(db_finance)
+    return db_finance
+
+
+@app.get("/finances/", response_model=Dict[str, Any])
+def read_finances(skip: int = 0, limit: int = 10, db: orm.Session = Depends(get_db)):
+    try:
+        total = db.query(Finance).count()
+        finances = db.query(Finance).offset(skip).limit(limit).all()
+        response = {
+            "total": total,
+            # Convert the list of Finance objects to a list of FinanceRead objects
+            "items": [FinanceRead.from_orm(finance) for finance in finances]
+        }
+        logging.info(f"Response: {response}")
+        return response
+    except Exception as e:
+        logging.error(f"Error occurred: {e}")
+        raise HTTPException(status_code=500, detail="Internal Server Error")
diff --git a/examples/tutorials/finance_manager/part3/Frontend/Finance/AddDialog.qml b/examples/tutorials/finance_manager/part3/Frontend/Finance/AddDialog.qml
new file mode 100644 (file)
index 0000000..b61c7f9
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Dialog {
+    id: dialog
+
+    signal finished(string itemName, string category, real cost, string date)
+
+    contentItem: ColumnLayout {
+        id: form
+        spacing: 10
+        property alias itemName: itemName
+        property alias category: category
+        property alias cost: cost
+        property alias date: date
+
+        GridLayout {
+            columns: 2
+            columnSpacing: 20
+            rowSpacing: 10
+            Layout.fillWidth: true
+
+            Label {
+                text: qsTr("Item Name:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: itemName
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            Label {
+                text: qsTr("Category:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: category
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            Label {
+                text: qsTr("Cost:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: cost
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+                placeholderText: qsTr("€")
+                inputMethodHints: Qt.ImhFormattedNumbersOnly
+            }
+
+            Label {
+                text: qsTr("Date:")
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+            }
+
+            TextField {
+                id: date
+                focus: true
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+                placeholderText: qsTr("dd-mm-yyyy")
+                validator: RegularExpressionValidator { regularExpression: /^[0-3]?\d-[01]?\d-\d{4}$/ }
+                // code to add the - automatically
+                onTextChanged: {
+                    if (date.text.length === 2 || date.text.length === 5) {
+                        date.text += "-"
+                    }
+                }
+                Component.onCompleted: {
+                var today = new Date();
+                var day = String(today.getDate()).padStart(2, '0');
+                var month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+                var year = today.getFullYear();
+                date.placeholderText = day + "-" + month + "-" + year;
+                }
+            }
+        }
+    }
+
+    function createEntry() {
+        form.itemName.clear()
+        form.category.clear()
+        form.cost.clear()
+        form.date.clear()
+        dialog.title = qsTr("Add Finance Item")
+        dialog.open()
+    }
+
+    x: parent.width / 2 - width / 2
+    y: parent.height / 2 - height / 2
+
+    focus: true
+    modal: true
+    title: qsTr("Add Finance Item")
+    standardButtons: Dialog.Ok | Dialog.Cancel
+
+    Component.onCompleted: {
+        dialog.visible = false
+        Qt.inputMethod.visibleChanged.connect(adjustDialogPosition)
+    }
+
+    function adjustDialogPosition() {
+        if (Qt.inputMethod.visible) {
+            // If the keyboard is visible, move the dialog up
+            dialog.y = parent.height / 4 - height / 2
+        } else {
+            // If the keyboard is not visible, center the dialog
+            dialog.y = parent.height / 2 - height / 2
+        }
+    }
+
+    onAccepted: {
+        finished(form.itemName.text, form.category.text, parseFloat(form.cost.text), form.date.text)
+    }
+}
diff --git a/examples/tutorials/finance_manager/part3/Frontend/Finance/FinanceDelegate.qml b/examples/tutorials/finance_manager/part3/Frontend/Finance/FinanceDelegate.qml
new file mode 100644 (file)
index 0000000..267d57d
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ItemDelegate {
+    id: delegate
+    checkable: true
+    width: parent.width
+    height: Qt.platform.os == "android" ?
+        Math.min(window.width, window.height) * 0.15 :
+        Math.min(window.width, window.height) * 0.1
+
+    contentItem:
+    RowLayout {
+        Label {
+            id: dateLabel
+            font.pixelSize: Qt.platform.os == "android" ?
+                Math.min(window.width, window.height) * 0.03 :
+                Math.min(window.width, window.height) * 0.02
+            text: date
+            elide: Text.ElideRight
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+            color: Material.primaryTextColor
+        }
+
+        ColumnLayout {
+            spacing: 5
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+
+            Label {
+                text: item_name
+                color: "#5c8540"
+                font.bold: true
+                elide: Text.ElideRight
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                Layout.fillWidth: true
+            }
+
+            Label {
+                text: category
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+        }
+
+        Item {
+        Layout.fillWidth: true  // This item will take up the remaining space
+        }
+
+        ColumnLayout {
+            spacing: 5
+            Layout.fillWidth: true
+            Layout.preferredWidth: 1
+
+            Label {
+                text: "you spent:"
+                color: "#5c8540"
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+
+            Label {
+                text: cost + "€"
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+            }
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part3/Frontend/Finance/FinancePieChart.qml b/examples/tutorials/finance_manager/part3/Frontend/Finance/FinancePieChart.qml
new file mode 100644 (file)
index 0000000..f538632
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+import QtQuick.Controls.Material
+
+Item {
+    width: Screen.width
+    height: Screen.height
+
+    GraphsView {
+        id: chart
+        anchors.fill: parent
+        antialiasing: true
+
+        theme: GraphsTheme {
+            colorScheme: Qt.Dark
+            theme: GraphsTheme.Theme.QtGreenNeon
+        }
+
+        PieSeries {
+            id: pieSeries
+        }
+    }
+
+    Text {
+        id: chartTitle
+        text: "Total Expenses Breakdown by Category"
+        color: "#5c8540"
+        font.pixelSize: Qt.platform.os == "android" ?
+            Math.min(window.width, window.height) * 0.04 :
+            Math.min(window.width, window.height) * 0.03
+        anchors.horizontalCenter: parent.horizontalCenter
+        anchors.top: parent.top
+        anchors.topMargin: 20
+    }
+
+    function updateChart(data) {
+        pieSeries.clear()
+        for (var category in data) {
+            var slice = pieSeries.append(category, data[category])
+            slice.label = category + ": " + data[category] + "€"
+            slice.labelVisible = true
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part3/Frontend/Finance/FinanceView.qml b/examples/tutorials/finance_manager/part3/Frontend/Finance/FinanceView.qml
new file mode 100644 (file)
index 0000000..8a943e0
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ListView {
+    id: listView
+    anchors.fill: parent
+    height: parent.height
+    property var financeModel
+
+    delegate: FinanceDelegate {
+        id: delegate
+        width: listView.width
+    }
+
+    model: financeModel
+
+    section.property: "month"  // Group items by the "month" property
+    section.criteria: ViewSection.FullString
+    section.delegate: Component {
+        id: sectionHeading
+        Rectangle {
+            width: listView.width
+            height:  Qt.platform.os == "android" ?
+                Math.min(window.width, window.height) * 0.05 :
+                Math.min(window.width, window.height) * 0.03
+            color: "#5c8540"
+
+            required property string section
+
+            Text {
+                text: parent.section
+                font.bold: true
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.03 :
+                    Math.min(window.width, window.height) * 0.02
+                color: Material.primaryTextColor
+            }
+        }
+    }
+
+    ScrollBar.vertical: ScrollBar { }
+}
diff --git a/examples/tutorials/finance_manager/part3/Frontend/Finance/Main.qml b/examples/tutorials/finance_manager/part3/Frontend/Finance/Main.qml
new file mode 100644 (file)
index 0000000..b06b3f5
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+import Finance
+
+ApplicationWindow {
+    id: window
+    Material.theme: Material.Dark
+    Material.accent: Material.Gray
+    width: Screen.width * 0.3
+    height: Screen.height * 0.5
+    visible: true
+    title: qsTr("Finance Manager")
+
+    // Add a toolbar for the application, only visible on mobile
+    header: ToolBar {
+        Material.primary: "#5c8540"
+        visible: Qt.platform.os == "android"
+        RowLayout {
+            anchors.fill: parent
+            Label {
+                text: qsTr("Finance Manager")
+                font.pixelSize: 20
+                Layout.alignment: Qt.AlignCenter
+            }
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        TabBar {
+            id: tabBar
+            Layout.fillWidth: true
+
+            TabButton {
+                text: qsTr("Expenses")
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                onClicked: stackView.currentIndex = 0
+            }
+
+            TabButton {
+                text: qsTr("Charts")
+                font.pixelSize: Qt.platform.os == "android" ?
+                    Math.min(window.width, window.height) * 0.04 :
+                    Math.min(window.width, window.height) * 0.02
+                onClicked: stackView.currentIndex = 1
+            }
+        }
+
+        StackLayout {
+            id: stackView
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            Item {
+                id: expensesView
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+
+                FinanceView {
+                    id: financeView
+                    anchors.fill: parent
+                    financeModel: finance_model
+                }
+            }
+
+            Item {
+                id: chartsView
+                Layout.fillWidth: true
+                Layout.fillHeight: true
+
+                FinancePieChart {
+                    id: financePieChart
+                    anchors.fill: parent
+                    Component.onCompleted: {
+                        var categoryData = finance_model.getCategoryData()
+                        updateChart(categoryData)
+                    }
+                }
+            }
+        }
+    }
+
+    // Model to store the finance data. Created from Python.
+    FinanceModel {
+        id: finance_model
+    }
+
+    // Add a dialog to add new entries
+    AddDialog {
+        id: addDialog
+        onFinished: function(item_name, category, cost, date) {
+            finance_model.append(item_name, category, cost, date)
+            var categoryData = finance_model.getCategoryData()
+            financePieChart.updateChart(categoryData)
+        }
+    }
+
+    // Add a button to open the dialog
+    ToolButton {
+        id: roundButton
+        text: qsTr("+")
+        highlighted: true
+        Material.elevation: 6
+        width: Qt.platform.os === "android" ?
+            Math.min(parent.width * 0.2, Screen.width * 0.15) :
+            Math.min(parent.width * 0.060, Screen.width * 0.05)
+        height: width  // Keep the button circular
+        anchors.margins: 10
+        anchors.right: parent.right
+        anchors.bottom: parent.bottom
+        background: Rectangle {
+            color: "#5c8540"
+            radius: roundButton.width / 2
+        }
+        font.pixelSize: width * 0.4
+        onClicked: {
+            addDialog.createEntry()
+        }
+    }
+}
diff --git a/examples/tutorials/finance_manager/part3/Frontend/Finance/qmldir b/examples/tutorials/finance_manager/part3/Frontend/Finance/qmldir
new file mode 100644 (file)
index 0000000..5362013
--- /dev/null
@@ -0,0 +1,6 @@
+module Finance
+Main 1.0 Main.qml
+FinanceView 1.0 FinanceView.qml
+FinancePieChart 1.0 FinancePieChart.qml
+FinanceDelegate 1.0 FinanceDelegate.qml
+AddDialog 1.0 AddDialog.qml
diff --git a/examples/tutorials/finance_manager/part3/Frontend/financemodel.py b/examples/tutorials/finance_manager/part3/Frontend/financemodel.py
new file mode 100644 (file)
index 0000000..0e4bbef
--- /dev/null
@@ -0,0 +1,109 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import requests
+from datetime import datetime
+from dataclasses import dataclass
+from enum import IntEnum
+from collections import defaultdict
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+                            QByteArray)
+from PySide6.QtQml import QmlElement
+
+QML_IMPORT_NAME = "Finance"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class FinanceModel(QAbstractListModel):
+
+    @QEnum
+    class FinanceRole(IntEnum):
+        ItemNameRole = Qt.ItemDataRole.DisplayRole
+        CategoryRole = Qt.ItemDataRole.UserRole
+        CostRole = Qt.ItemDataRole.UserRole + 1
+        DateRole = Qt.ItemDataRole.UserRole + 2
+        MonthRole = Qt.ItemDataRole.UserRole + 3
+
+    @dataclass
+    class Finance:
+        item_name: str
+        category: str
+        cost: float
+        date: str
+
+        @property
+        def month(self):
+            return datetime.strptime(self.date, "%d-%m-%Y").strftime("%B %Y")
+
+    def __init__(self, parent=None) -> None:
+        super().__init__(parent)
+        self.m_finances = []
+        self.fetchAllData()
+
+    def fetchAllData(self):
+        response = requests.get("http://127.0.0.1:8000/finances/")
+        try:
+            data = response.json()
+        except requests.exceptions.JSONDecodeError:
+            print("Failed to decode JSON response")
+            return
+        self.beginInsertRows(QModelIndex(), 0, len(data["items"]) - 1)
+        self.m_finances.extend([self.Finance(**item) for item in data["items"]])
+        self.endInsertRows()
+
+    def rowCount(self, parent=QModelIndex()):
+        return len(self.m_finances)
+
+    def data(self, index: QModelIndex, role: int):
+        if not index.isValid() or index.row() >= self.rowCount():
+            return None
+        row = index.row()
+        if row < self.rowCount():
+            finance = self.m_finances[row]
+            if role == FinanceModel.FinanceRole.ItemNameRole:
+                return finance.item_name
+            if role == FinanceModel.FinanceRole.CategoryRole:
+                return finance.category
+            if role == FinanceModel.FinanceRole.CostRole:
+                return finance.cost
+            if role == FinanceModel.FinanceRole.DateRole:
+                return finance.date
+            if role == FinanceModel.FinanceRole.MonthRole:
+                return finance.month
+        return None
+
+    def roleNames(self):
+        roles = super().roleNames()
+        roles[FinanceModel.FinanceRole.ItemNameRole] = QByteArray(b"item_name")
+        roles[FinanceModel.FinanceRole.CategoryRole] = QByteArray(b"category")
+        roles[FinanceModel.FinanceRole.CostRole] = QByteArray(b"cost")
+        roles[FinanceModel.FinanceRole.DateRole] = QByteArray(b"date")
+        roles[FinanceModel.FinanceRole.MonthRole] = QByteArray(b"month")
+        return roles
+
+    @Slot(int, result='QVariantMap')
+    def get(self, row: int):
+        finance = self.m_finances[row]
+        return {"item_name": finance.item_name, "category": finance.category,
+                "cost": finance.cost, "date": finance.date}
+
+    @Slot(str, str, float, str)
+    def append(self, item_name: str, category: str, cost: float, date: str):
+        finance = {"item_name": item_name, "category": category, "cost": cost, "date": date}
+        response = requests.post("http://127.0.0.1:8000/finances/", json=finance)
+        if response.status_code == 200:
+            finance = response.json()
+            self.beginInsertRows(QModelIndex(), 0, 0)
+            self.m_finances.insert(0, self.Finance(**finance))
+            self.endInsertRows()
+        else:
+            print("Failed to add finance item")
+
+    @Slot(result=dict)
+    def getCategoryData(self):
+        category_data = defaultdict(float)
+        for finance in self.m_finances:
+            category_data[finance.category] += finance.cost
+        return dict(category_data)
diff --git a/examples/tutorials/finance_manager/part3/Frontend/main.py b/examples/tutorials/finance_manager/part3/Frontend/main.py
new file mode 100644 (file)
index 0000000..bb90d00
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtWidgets import QApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from financemodel import FinanceModel  # noqa: F401
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    QApplication.setOrganizationName("QtProject")
+    QApplication.setApplicationName("Finance Manager")
+    engine = QQmlApplicationEngine()
+
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("Finance", "Main")
+
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
diff --git a/examples/tutorials/finance_manager/part3/doc/part3.md b/examples/tutorials/finance_manager/part3/doc/part3.md
new file mode 100644 (file)
index 0000000..08768f3
--- /dev/null
@@ -0,0 +1,11 @@
+# Finance Manager Example - Part 3
+
+This example represents the final part of the tutorial series on creating a simple Finance Manager
+that allows users to manage their expenses and visualize them using a pie chart, using PySide6,
+[SQLAlchemy], [FastAPI], and [Pydantic].
+
+For more details, see the [Finance Manager Tutorial - Part 3](tutorial_financemanager_partthree).
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
diff --git a/examples/tutorials/finance_manager/part3/part3.pyproject b/examples/tutorials/finance_manager/part3/part3.pyproject
new file mode 100644 (file)
index 0000000..c51cb91
--- /dev/null
@@ -0,0 +1,16 @@
+{
+    "files": [
+        "Backend/database.py",
+        "Backend/main.py",
+        "Backend/rest_api.py",
+        "Frontend/Finance/AddDialog.qml",
+        "Frontend/Finance/FinanceDelegate.qml",
+        "Frontend/Finance/FinancePieChart.qml",
+        "Frontend/Finance/FinanceView.qml",
+        "Frontend/Finance/Main.qml",
+        "Frontend/Finance/qmldir",
+        "Frontend/financemodel.py",
+        "Frontend/main.py",
+        "requirements.txt"
+    ]
+}
diff --git a/examples/tutorials/finance_manager/part3/requirements.txt b/examples/tutorials/finance_manager/part3/requirements.txt
new file mode 100644 (file)
index 0000000..1970bea
--- /dev/null
@@ -0,0 +1,3 @@
+sqlalchemy
+uvicorn
+fastapi
diff --git a/examples/uitools/uiloader/doc/uiloader.rst b/examples/uitools/uiloader/doc/uiloader.rst
new file mode 100644 (file)
index 0000000..df02c9b
--- /dev/null
@@ -0,0 +1,7 @@
+UILoader Example
+================
+
+This example demonstrates how to dynamically load and display a user interface
+designed with Qt Designer using PySide6's `QUiLoader`. It shows how to load a
+`.ui` file at runtime, allowing for flexible and dynamic UI design and
+modification without recompiling the application.
index 265c938378f8bff57e6e9a86369d9d353a589106..01530d6549f0d685c01df2406878e2a88fd9fc7d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """QUiLoader example, showing how to dynamically load a Qt Designer form
    from a UI file."""
@@ -21,7 +22,7 @@ if __name__ == '__main__':
 
     app = QApplication(sys.argv)
     ui_file = QFile(ui_file_name)
-    if not ui_file.open(QIODevice.ReadOnly):
+    if not ui_file.open(QIODevice.OpenModeFlag.ReadOnly):
         reason = ui_file.errorString()
         print(f"Cannot open {ui_file_name}: {reason}")
         sys.exit(-1)
index d3067eec14c4f9fcf6a366d1cf477d1cf93a0ae5..c4bb873ee48397815c017fc2ae1da2b5b99e1781 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sysconfig
 from enum import Enum
@@ -202,7 +203,10 @@ def python_version():
 
 
 def get_python_include_path():
-    return sysconfig.get_path('include')
+    if sys.platform == 'win32':
+        return sysconfig.get_path('include')
+    else:
+        return sysconfig.get_path('include', scheme="posix_prefix")
 
 
 def python_link_flags_qmake():
index 2d0cb0286d4c734d1d54bebcf4f257b59ef3ca8a..987a4ee1a3eb3b2bbd9be6fad274653ad374c3ab 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtCore import QObject, Signal, Slot
index 7f2413e6cb09b365ef351230ae834b46e07761a9..27cef86a7b04ca08df75b7ba774dafe73973b10f 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtCore import Signal, Slot
index 8c04aa64f845804614fc111efd7068e750b7616b..b344ec795e0e935a3b163b5f935579cf6f2e87ce 100644 (file)
@@ -1,6 +1,8 @@
-# Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+# Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com,
+#   author Milian Wolff <milian.wolff@kdab.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 import os
@@ -31,8 +33,8 @@ if __name__ == '__main__':
 
     # setup the QWebSocketServer
     server = QWebSocketServer("QWebChannel Standalone Example Server",
-                              QWebSocketServer.NonSecureMode)
-    if not server.listen(QHostAddress.LocalHost, 12345):
+                              QWebSocketServer.SslMode.NonSecureMode)
+    if not server.listen(QHostAddress.SpecialAddress.LocalHost, 12345):
         print("Failed to open web socket server.")
         sys.exit(-1)
 
index 36c2fe400a057cf05188177b8c84c07969cf7735..4543c4c830c0d24efa756d574783a377b404a9af 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'dialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
index 00b410891cf1df6d3d8282ba929c11d17cc43787..a14672f250f7254fa65ed8935f6194883a1f5aad 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QObject, Signal, Slot
 
index ab60e8448b6b66d33388e6cf0635fdb7d0527369..96e9b822a1f25ae78ddffb2f7aa8913466e6d537 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWebChannel import QWebChannelAbstractTransport
 from PySide6.QtCore import QByteArray, QJsonDocument, Slot
index aee79c2aa59d09d1b87606899e88a80d9f61dc87..e5c667c5140234efe30ae7cec9be82843bada8f8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 WebEngine QtQuick 2 Example"""
 
@@ -67,4 +68,6 @@ if __name__ == '__main__':
 
     QMetaObject.invokeMethod(engine.rootObjects()[0], "load", Q_ARG("QVariant", url))
 
-    app.exec()
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
diff --git a/examples/webenginewidgets/markdowneditor/doc/markdowneditor.png b/examples/webenginewidgets/markdowneditor/doc/markdowneditor.png
new file mode 100644 (file)
index 0000000..9f456c4
Binary files /dev/null and b/examples/webenginewidgets/markdowneditor/doc/markdowneditor.png differ
diff --git a/examples/webenginewidgets/markdowneditor/doc/markdowneditor.rst b/examples/webenginewidgets/markdowneditor/doc/markdowneditor.rst
new file mode 100644 (file)
index 0000000..d54aafa
--- /dev/null
@@ -0,0 +1,11 @@
+WebEngine Markdown Editor Example
+=================================
+
+Demonstrates how to integrate a web engine in a hybrid desktop application.
+It corresponds to the Qt example `WebEngine Markdown Editor Example
+<https://doc.qt.io/qt-5/qtwebengine-webenginewidgets-markdowneditor-example.html>`_.
+
+.. image:: markdowneditor.png
+    :align: center
+    :alt: markdowneditor screenshot
+    :width: 400
index 331fbc0ca9071a76cb3196b67312807129cc2acd..10ed57523c7afda6d57b4cb311837fe8e3c38b21 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtCore import QObject, Property, Signal
index 4d787f0f06d9812b16c131fb502e2e0f2619dda6..8a92abf8dd50b163f68ae843456bff3115af2921 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 Markdown Editor Example"""
 
index 6f74cf93d86c61fcd4bee6717a61d1ccf447594f..208e6fa0fe6e48c0d7fd85d2ae2c59bd02cd4b76 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtCore import QDir, QFile, QIODevice, QUrl, Qt, Slot
@@ -20,9 +21,9 @@ class MainWindow(QMainWindow):
         self.m_content = Document()
         self._ui = Ui_MainWindow()
         self._ui.setupUi(self)
-        font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
+        font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
         self._ui.editor.setFont(font)
-        self._ui.preview.setContextMenuPolicy(Qt.NoContextMenu)
+        self._ui.preview.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
         self._page = PreviewPage(self)
         self._ui.preview.setPage(self._page)
 
@@ -43,7 +44,7 @@ class MainWindow(QMainWindow):
         self._ui.editor.document().modificationChanged.connect(self._ui.actionSave.setEnabled)
 
         defaultTextFile = QFile(":/default.md")
-        defaultTextFile.open(QIODevice.ReadOnly)
+        defaultTextFile.open(QIODevice.OpenModeFlag.ReadOnly)
         data = defaultTextFile.readAll()
         self._ui.editor.setPlainText(data.data().decode('utf8'))
 
@@ -55,7 +56,7 @@ class MainWindow(QMainWindow):
     def openFile(self, path):
         f = QFile(path)
         name = QDir.toNativeSeparators(path)
-        if not f.open(QIODevice.ReadOnly):
+        if not f.open(QIODevice.OpenModeFlag.ReadOnly):
             error = f.errorString()
             QMessageBox.warning(self, self.windowTitle(),
                                 f"Could not open file {name}: {error}")
@@ -73,7 +74,7 @@ class MainWindow(QMainWindow):
         if self.isModified():
             m = "You have unsaved changes. Do you want to create a new document anyway?"
             button = QMessageBox.question(self, self.windowTitle(), m)
-            if button != QMessageBox.Yes:
+            if button != QMessageBox.StandardButton.Yes:
                 return
 
         self.m_file_path = ''
@@ -85,13 +86,13 @@ class MainWindow(QMainWindow):
         if self.isModified():
             m = "You have unsaved changes. Do you want to open a new document anyway?"
             button = QMessageBox.question(self, self.windowTitle(), m)
-            if button != QMessageBox.Yes:
+            if button != QMessageBox.StandardButton.Yes:
                 return
         dialog = QFileDialog(self)
         dialog.setWindowTitle("Open MarkDown File")
         dialog.setMimeTypeFilters(["text/markdown"])
-        dialog.setAcceptMode(QFileDialog.AcceptOpen)
-        if dialog.exec() == QDialog.Accepted:
+        dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
+        if dialog.exec() == QDialog.DialogCode.Accepted:
             self.openFile(dialog.selectedFiles()[0])
 
     @Slot()
@@ -103,7 +104,7 @@ class MainWindow(QMainWindow):
 
         f = QFile(self.m_file_path)
         name = QDir.toNativeSeparators(self.m_file_path)
-        if not f.open(QIODevice.WriteOnly | QIODevice.Text):
+        if not f.open(QIODevice.OpenModeFlag.WriteOnly | QIODevice.OpenModeFlag.Text):
             error = f.errorString()
             QMessageBox.warning(self, self.windowTitle(),
                                 f"Could not write to file {name}: {error}")
@@ -119,9 +120,9 @@ class MainWindow(QMainWindow):
         dialog = QFileDialog(self)
         dialog.setWindowTitle("Save MarkDown File")
         dialog.setMimeTypeFilters(["text/markdown"])
-        dialog.setAcceptMode(QFileDialog.AcceptSave)
+        dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
         dialog.setDefaultSuffix("md")
-        if dialog.exec() != QDialog.Accepted:
+        if dialog.exec() != QDialog.DialogCode.Accepted:
             return
         path = dialog.selectedFiles()[0]
         self.m_file_path = path
@@ -131,7 +132,7 @@ class MainWindow(QMainWindow):
         if self.isModified():
             m = "You have unsaved changes. Do you want to exit anyway?"
             button = QMessageBox.question(self, self.windowTitle(), m)
-            if button != QMessageBox.Yes:
+            if button != QMessageBox.StandardButton.Yes:
                 event.ignore()
             else:
                 event.accept()
index f4e29ad95597a5d35698d524e2c78b2ebbf49e5d..846f2d70a10ba5a53060b21286cd48f6cd0219bf 100644 (file)
@@ -18,7 +18,7 @@
     <item>
      <widget class="QSplitter" name="splitter">
       <property name="orientation">
-       <enum>Qt::Horizontal</enum>
+       <enum>Qt::Orientation::Horizontal</enum>
       </property>
       <widget class="QPlainTextEdit" name="editor"/>
       <widget class="QWebEngineView" name="preview" native="true"/>
    </property>
   </action>
  </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QWebEngineView</class>
+   <extends>QWidget</extends>
+   <header>qwebengineview.h</header>
+  </customwidget>
+ </customwidgets>
  <resources/>
  <connections/>
 </ui>
index 35ac80be4893985c78c77c59d5b98f7b16e7a864..75c8e317fb2d7b95f3cd70c71a5b0b9f6318e32d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtGui import QDesktopServices
 from PySide6.QtWebEngineCore import QWebEnginePage
index c8e30b49be9665cb8f12d6068268836538ff92c1..2d3fce9032ad64b5a90b9ebf551324dd011c16a0 100644 (file)
@@ -27,6 +27,3 @@
   </script>
 </body>
 </html>
-
-
-
index 0be7691192c44821998122e7bcf17d02336a142c..d538824f5dc46dcf29c941b260da3ba061f3e24e 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'mainwindow.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -42,7 +42,7 @@ class Ui_MainWindow(object):
         self.horizontalLayout.setObjectName(u"horizontalLayout")
         self.splitter = QSplitter(self.centralwidget)
         self.splitter.setObjectName(u"splitter")
-        self.splitter.setOrientation(Qt.Horizontal)
+        self.splitter.setOrientation(Qt.Orientation.Horizontal)
         self.editor = QPlainTextEdit(self.splitter)
         self.editor.setObjectName(u"editor")
         self.splitter.addWidget(self.editor)
index b59aead97cccb21cce0fa1d09a8f18d007e1246f..6a96c360a707536f309084617d3046ab1431f747 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 WebEngineWidgets Notifications Example"""
 
@@ -37,10 +38,11 @@ if __name__ == '__main__':
     view.setPage(WebEnginePage(view))
 
     def set_feature_permission(origin: QUrl, feature: QWebEnginePage.Feature):
-        if feature != QWebEnginePage.Notifications:
+        if feature != QWebEnginePage.Feature.Notifications:
             return
 
-        view.page().setFeaturePermission(origin, feature, QWebEnginePage.PermissionGrantedByUser)
+        view.page().setFeaturePermission(origin, feature,
+                                         QWebEnginePage.PermissionPolicy.PermissionGrantedByUser)
 
     view.page().featurePermissionRequested.connect(set_feature_permission)
     profile = view.page().profile()
index e68ce3d6f3613fda1b1aa3e9db8da52b45dc2e85..86ff5c8e416c7266eab2980c2fe2670cd9dc75d5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, QTimer, QPoint, Slot
 from PySide6.QtWidgets import (QWidget, QHBoxLayout, QLabel, QVBoxLayout, QSpacerItem, QSizePolicy,
@@ -13,7 +14,7 @@ class NotificationPopup(QWidget):
         super().__init__(parent)
         self.notification = None
         self.m_icon, self.m_title, self.m_message = QLabel(), QLabel(), QLabel()
-        self.setWindowFlags(Qt.ToolTip)
+        self.setWindowFlags(Qt.WindowType.ToolTip)
 
         rootLayout = QHBoxLayout(self)
         rootLayout.addWidget(self.m_icon)
@@ -25,7 +26,7 @@ class NotificationPopup(QWidget):
         bodyLayout.addLayout(titleLayout)
 
         titleLayout.addWidget(self.m_title)
-        titleLayout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding))
+        titleLayout.addItem(QSpacerItem(0, 0, QSizePolicy.Policy.Expanding))
 
         close = QPushButton("Close")
         titleLayout.addWidget(close)
@@ -63,6 +64,6 @@ class NotificationPopup(QWidget):
 
     def mouseReleaseEvent(self, event: QMouseEvent) -> None:
         QWidget.mouseReleaseEvent(event)
-        if self.notification and event.button() == Qt.LeftButton:
+        if self.notification and event.button() == Qt.MouseButton.LeftButton:
             self.notification.click()
             self.onClosed()
index a124ea084df093064246a2f66efc6df0570cd6dd..1bf458e79ff4e7beaaf6398b8d949d4a31ac6318 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWebEngineCore import (qWebEngineChromiumVersion,
                                      QWebEngineProfile, QWebEngineSettings)
@@ -19,7 +20,7 @@ class Browser(QObject):
 
         # Quit application if the download manager window is the only
         # remaining window
-        self._download_manager_widget.setAttribute(Qt.WA_QuitOnClose, False)
+        self._download_manager_widget.setAttribute(Qt.WidgetAttribute.WA_QuitOnClose, False)
 
         dp = QWebEngineProfile.defaultProfile()
         dp.downloadRequested.connect(self._download_manager_widget.download_requested)
@@ -29,10 +30,10 @@ class Browser(QObject):
             name = "simplebrowser." + qWebEngineChromiumVersion()
             self._profile = QWebEngineProfile(name)
             s = self._profile.settings()
-            s.setAttribute(QWebEngineSettings.PluginsEnabled, True)
-            s.setAttribute(QWebEngineSettings.DnsPrefetchEnabled, True)
-            s.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
-            s.setAttribute(QWebEngineSettings.LocalContentCanAccessFileUrls, False)
+            s.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
+            s.setAttribute(QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True)
+            s.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
+            s.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, False)
             self._profile.downloadRequested.connect(
                 self._download_manager_widget.download_requested)
 
index 43b8112004eef19347fc0af20e2b3060f0d4df23..ad28304ef93722579b0e050405b52572f770b21f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -17,7 +18,7 @@ def remove_backspace(keys):
     result = keys.copy()
     # Chromium already handles navigate on backspace when appropriate.
     for i, key in enumerate(result):
-        if (key[0].key() & Qt.Key_unknown) == Qt.Key_Backspace:
+        if (key[0].key() & Qt.Key.Key_unknown) == Qt.Key.Key_Backspace:
             del result[i]
             break
     return result
@@ -50,8 +51,8 @@ class BrowserWindow(QMainWindow):
         self._reload_icon = QIcon.fromTheme(QIcon.ThemeIcon.ViewRefresh,
                                             QIcon(":view-refresh.png"))
 
-        self.setAttribute(Qt.WA_DeleteOnClose, True)
-        self.setFocusPolicy(Qt.ClickFocus)
+        self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
+        self.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
 
         if not forDevTools:
             self._progress_bar = QProgressBar(self)
@@ -96,7 +97,7 @@ class BrowserWindow(QMainWindow):
 
             focus_url_line_edit_action = QAction(self)
             self.addAction(focus_url_line_edit_action)
-            focus_url_line_edit_action.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_L))
+            focus_url_line_edit_action.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_L))
             focus_url_line_edit_action.triggered.connect(self._focus_url_lineEdit)
 
         self.handle_web_view_title_changed("")
@@ -139,27 +140,27 @@ class BrowserWindow(QMainWindow):
 
     def create_file_menu(self, tabWidget):
         file_menu = QMenu("File")
-        file_menu.addAction("&New Window", QKeySequence.New,
+        file_menu.addAction("&New Window", QKeySequence.StandardKey.New,
                             self.handle_new_window_triggered)
         file_menu.addAction("New &Incognito Window",
                             self.handle_new_incognito_window_triggered)
 
         new_tab_action = QAction("New Tab", self)
-        new_tab_action.setShortcuts(QKeySequence.AddTab)
+        new_tab_action.setShortcuts(QKeySequence.StandardKey.AddTab)
         new_tab_action.triggered.connect(self._new_tab)
         file_menu.addAction(new_tab_action)
 
-        file_menu.addAction("&Open File...", QKeySequence.Open,
+        file_menu.addAction("&Open File...", QKeySequence.StandardKey.Open,
                             self.handle_file_open_triggered)
         file_menu.addSeparator()
 
         close_tab_action = QAction("Close Tab", self)
-        close_tab_action.setShortcuts(QKeySequence.Close)
+        close_tab_action.setShortcuts(QKeySequence.StandardKey.Close)
         close_tab_action.triggered.connect(self._close_current_tab)
         file_menu.addAction(close_tab_action)
 
         self._close_action = QAction("Quit", self)
-        self._close_action.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_Q))
+        self._close_action.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q))
         self._close_action.triggered.connect(self.close)
         file_menu.addAction(self._close_action)
 
@@ -181,15 +182,15 @@ class BrowserWindow(QMainWindow):
     def create_edit_menu(self):
         edit_menu = QMenu("Edit")
         find_action = edit_menu.addAction("Find")
-        find_action.setShortcuts(QKeySequence.Find)
+        find_action.setShortcuts(QKeySequence.StandardKey.Find)
         find_action.triggered.connect(self.handle_find_action_triggered)
 
         find_next_action = edit_menu.addAction("Find Next")
-        find_next_action.setShortcut(QKeySequence.FindNext)
+        find_next_action.setShortcut(QKeySequence.StandardKey.FindNext)
         find_next_action.triggered.connect(self._find_next)
 
         find_previous_action = edit_menu.addAction("Find Previous")
-        find_previous_action.setShortcut(QKeySequence.FindPrevious)
+        find_previous_action.setShortcut(QKeySequence.StandardKey.FindPrevious)
         find_previous_action.triggered.connect(self._find_previous)
         return edit_menu
 
@@ -242,25 +243,25 @@ class BrowserWindow(QMainWindow):
         view_menu = QMenu("View")
         self._stop_action = view_menu.addAction("Stop")
         shortcuts = []
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Period))
-        shortcuts.append(QKeySequence(Qt.Key_Escape))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Period))
+        shortcuts.append(QKeySequence(Qt.Key.Key_Escape))
         self._stop_action.setShortcuts(shortcuts)
         self._stop_action.triggered.connect(self._stop)
 
         self._reload_action = view_menu.addAction("Reload Page")
-        self._reload_action.setShortcuts(QKeySequence.Refresh)
+        self._reload_action.setShortcuts(QKeySequence.StandardKey.Refresh)
         self._reload_action.triggered.connect(self._reload)
 
         zoom_in = view_menu.addAction("Zoom In")
-        zoom_in.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_Plus))
+        zoom_in.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Plus))
         zoom_in.triggered.connect(self._zoom_in)
 
         zoom_out = view_menu.addAction("Zoom Out")
-        zoom_out.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_Minus))
+        zoom_out.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Minus))
         zoom_out.triggered.connect(self._zoom_out)
 
         reset_zoom = view_menu.addAction("Reset Zoom")
-        reset_zoom.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_0))
+        reset_zoom.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_0))
         reset_zoom.triggered.connect(self._reset_zoom)
 
         view_menu.addSeparator()
@@ -285,25 +286,25 @@ class BrowserWindow(QMainWindow):
         menu = QMenu("Window")
         self._next_tab_action = QAction("Show Next Tab", self)
         shortcuts = []
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceRight))
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageDown))
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketRight))
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Less))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BraceRight))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_PageDown))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BracketRight))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Less))
         self._next_tab_action.setShortcuts(shortcuts)
         self._next_tab_action.triggered.connect(tabWidget.next_tab)
 
         self._previous_tab_action = QAction("Show Previous Tab", self)
         shortcuts.clear()
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceLeft))
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageUp))
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketLeft))
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Greater))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BraceLeft))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_PageUp))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BracketLeft))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Greater))
         self._previous_tab_action.setShortcuts(shortcuts)
         self._previous_tab_action.triggered.connect(tabWidget.previous_tab)
 
         self._inspector_action = QAction("Open inspector in window", self)
         shortcuts.clear()
-        shortcuts.append(QKeySequence(Qt.CTRL | Qt.SHIFT | Qt.Key_I))
+        shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.SHIFT | Qt.Key.Key_I))
         self._inspector_action.setShortcuts(shortcuts)
         self._inspector_action.triggered.connect(self._emit_dev_tools_requested)
         self._window_menu = menu
@@ -336,11 +337,11 @@ class BrowserWindow(QMainWindow):
 
     @Slot()
     def _back(self):
-        self._tab_widget.trigger_web_page_action(QWebEnginePage.Back)
+        self._tab_widget.trigger_web_page_action(QWebEnginePage.WebAction.Back)
 
     @Slot()
     def _forward(self):
-        self._tab_widget.trigger_web_page_action(QWebEnginePage.Forward)
+        self._tab_widget.trigger_web_page_action(QWebEnginePage.WebAction.Forward)
 
     @Slot()
     def _stop_reload(self):
@@ -353,10 +354,10 @@ class BrowserWindow(QMainWindow):
         navigation_bar.toggleViewAction().setEnabled(False)
 
         self._history_back_action = QAction(self)
-        back_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.Back))
+        back_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.StandardKey.Back))
 
         # For some reason Qt doesn't bind the dedicated Back key to Back.
-        back_shortcuts.append(QKeySequence(Qt.Key_Back))
+        back_shortcuts.append(QKeySequence(Qt.Key.Key_Back))
         self._history_back_action.setShortcuts(back_shortcuts)
         self._history_back_action.setIconVisibleInMenu(False)
         back_icon = QIcon.fromTheme(QIcon.ThemeIcon.GoPrevious,
@@ -367,8 +368,8 @@ class BrowserWindow(QMainWindow):
         navigation_bar.addAction(self._history_back_action)
 
         self._history_forward_action = QAction(self)
-        fwd_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.Forward))
-        fwd_shortcuts.append(QKeySequence(Qt.Key_Forward))
+        fwd_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.StandardKey.Forward))
+        fwd_shortcuts.append(QKeySequence(Qt.Key.Key_Forward))
         self._history_forward_action.setShortcuts(fwd_shortcuts)
         self._history_forward_action.setIconVisibleInMenu(False)
         next_icon = QIcon.fromTheme(QIcon.ThemeIcon.GoNext,
@@ -384,7 +385,7 @@ class BrowserWindow(QMainWindow):
 
         self._url_line_edit = QLineEdit(self)
         self._fav_action = QAction(self)
-        self._url_line_edit.addAction(self._fav_action, QLineEdit.LeadingPosition)
+        self._url_line_edit.addAction(self._fav_action, QLineEdit.ActionPosition.LeadingPosition)
         self._url_line_edit.setClearButtonEnabled(True)
         navigation_bar.addWidget(self._url_line_edit)
 
@@ -398,13 +399,13 @@ class BrowserWindow(QMainWindow):
         return navigation_bar
 
     def handle_web_action_enabled_changed(self, action, enabled):
-        if action == QWebEnginePage.Back:
+        if action == QWebEnginePage.WebAction.Back:
             self._history_back_action.setEnabled(enabled)
-        elif action == QWebEnginePage.Forward:
+        elif action == QWebEnginePage.WebAction.Forward:
             self._history_forward_action.setEnabled(enabled)
-        elif action == QWebEnginePage.Reload:
+        elif action == QWebEnginePage.WebAction.Reload:
             self._reload_action.setEnabled(enabled)
-        elif action == QWebEnginePage.Stop:
+        elif action == QWebEnginePage.WebAction.Stop:
             self._stop_action.setEnabled(enabled)
         else:
             print("Unhandled webActionChanged signal", file=sys.stderr)
@@ -436,7 +437,7 @@ class BrowserWindow(QMainWindow):
         if not self.current_tab():
             return
         search, ok = QInputDialog.getText(self, "Find", "Find:",
-                                          QLineEdit.Normal, self._last_search)
+                                          QLineEdit.EchoMode.Normal, self._last_search)
         if ok and search:
             self._last_search = search
             self.current_tab().findText(self._last_search)
@@ -464,12 +465,12 @@ class BrowserWindow(QMainWindow):
 
     def handle_web_view_load_progress(self, progress):
         if 0 < progress and progress < 100:
-            self._stop_reload_action.setData(QWebEnginePage.Stop)
+            self._stop_reload_action.setData(QWebEnginePage.WebAction.Stop)
             self._stop_reload_action.setIcon(self._stop_icon)
             self._stop_reload_action.setToolTip("Stop loading the current page")
             self._progress_bar.setValue(progress)
         else:
-            self._stop_reload_action.setData(QWebEnginePage.Reload)
+            self._stop_reload_action.setData(QWebEnginePage.WebAction.Reload)
             self._stop_reload_action.setIcon(self._reload_icon)
             self._stop_reload_action.setToolTip("Reload the current page")
             self._progress_bar.setValue(0)
@@ -485,12 +486,12 @@ class BrowserWindow(QMainWindow):
     def handle_dev_tools_requested(self, source):
         page = self._browser.create_dev_tools_window().current_tab().page()
         source.setDevToolsPage(page)
-        source.triggerAction(QWebEnginePage.InspectElement)
+        source.triggerAction(QWebEnginePage.WebAction.InspectElement)
 
     def handle_find_text_finished(self, result):
         sb = self.statusBar()
         if result.numberOfMatches() == 0:
-            sb.showMessage(f'"{self._lastSearch}" not found.')
+            sb.showMessage(f'"{self._last_search}" not found.')
         else:
             active = result.activeMatch()
             number = result.numberOfMatches()
index a97f25b6e01c06b8065ec98d75567f21c541186f..7e8c36075739e1609defbf3dd73c28aec0a3d32e 100644 (file)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>370</width>
-    <height>141</height>
+    <width>689</width>
+    <height>204</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -26,7 +26,7 @@
       <string>Icon</string>
      </property>
      <property name="alignment">
-      <set>Qt::AlignCenter</set>
+      <set>Qt::AlignmentFlag::AlignCenter</set>
      </property>
     </widget>
    </item>
@@ -42,7 +42,7 @@
       <string>Error</string>
      </property>
      <property name="alignment">
-      <set>Qt::AlignCenter</set>
+      <set>Qt::AlignmentFlag::AlignCenter</set>
      </property>
      <property name="wordWrap">
       <bool>true</bool>
@@ -63,7 +63,7 @@
 Do you wish to override the security check and continue ?   </string>
      </property>
      <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+      <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
      </property>
      <property name="wordWrap">
       <bool>true</bool>
@@ -73,7 +73,7 @@ Do you wish to override the security check and continue ?   </string>
    <item>
     <spacer name="verticalSpacer">
      <property name="orientation">
-      <enum>Qt::Vertical</enum>
+      <enum>Qt::Orientation::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
@@ -86,10 +86,10 @@ Do you wish to override the security check and continue ?   </string>
    <item>
     <widget class="QDialogButtonBox" name="buttonBox">
      <property name="orientation">
-      <enum>Qt::Horizontal</enum>
+      <enum>Qt::Orientation::Horizontal</enum>
      </property>
      <property name="standardButtons">
-      <set>QDialogButtonBox::No|QDialogButtonBox::Yes</set>
+      <set>QDialogButtonBox::StandardButton::No|QDialogButtonBox::StandardButton::Yes</set>
      </property>
     </widget>
    </item>
index 7096b8b571ee5616cbf2394400964e54988e0d8a..09dc00611478e0e5bcf4d6593deced8f5315baca 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWebEngineCore import QWebEngineDownloadRequest
 from PySide6.QtWidgets import QWidget, QFileDialog
index b7544ac16ce20fe14fd9b1f124d01b86bee73ca6..aa3b7da2112d0cdc288e2d226395e593376c9a4e 100644 (file)
@@ -20,7 +20,7 @@
   </property>
   <layout class="QVBoxLayout" name="m_topLevelLayout">
    <property name="sizeConstraint">
-    <enum>QLayout::SetNoConstraint</enum>
+    <enum>QLayout::SizeConstraint::SetNoConstraint</enum>
    </property>
    <property name="leftMargin">
     <number>0</number>
 }</string>
      </property>
      <property name="verticalScrollBarPolicy">
-      <enum>Qt::ScrollBarAlwaysOn</enum>
+      <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum>
      </property>
      <property name="horizontalScrollBarPolicy">
-      <enum>Qt::ScrollBarAlwaysOff</enum>
+      <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
      </property>
      <property name="widgetResizable">
       <bool>true</bool>
      </property>
      <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+      <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
      </property>
      <widget class="QWidget" name="m_items">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>382</width>
+        <height>208</height>
+       </rect>
+      </property>
       <property name="styleSheet">
        <string notr="true">#m_items {background: palette(mid)}</string>
       </property>
@@ -89,7 +97,7 @@
           <string>No downloads</string>
          </property>
          <property name="alignment">
-          <set>Qt::AlignCenter</set>
+          <set>Qt::AlignmentFlag::AlignCenter</set>
          </property>
         </widget>
        </item>
index 3b4973cb810deabb1a33e111a706e10a76d004a8..39a1105380c69e8bbda5415eea1d544dda2887e1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from ui_downloadwidget import Ui_DownloadWidget
 
index 47f621486d317476ab27cc9a8d82042e358555d0..9017f20439e8089091ba8a70ec0a91f3d09d2673 100644 (file)
@@ -2,6 +2,14 @@
 <ui version="4.0">
  <class>DownloadWidget</class>
  <widget class="QFrame" name="DownloadWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>144</width>
+    <height>103</height>
+   </rect>
+  </property>
   <property name="styleSheet">
    <string notr="true">#DownloadWidget {
   background: palette(button);
@@ -11,7 +19,7 @@
   </property>
   <layout class="QGridLayout" name="m_topLevelLayout">
    <property name="sizeConstraint">
-    <enum>QLayout::SetMinAndMaxSize</enum>
+    <enum>QLayout::SizeConstraint::SetMinAndMaxSize</enum>
    </property>
    <item row="0" column="0">
     <widget class="QLabel" name="m_dstName">
    <item row="0" column="1">
     <widget class="QPushButton" name="m_cancelButton">
      <property name="sizePolicy">
-      <sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
+      <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
      </property>
      <property name="styleSheet">
       <string notr="true">QPushButton {
index 781ec29eb4654c6a2f7f5a6a6b631ffd22392dd3..2602c5db92e2b7453f58e3e7fd36a899afb0a608 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt WebEngineWidgets Simple Browser example from Qt v6.x"""
 
@@ -33,8 +34,8 @@ if __name__ == "__main__":
     QLoggingCategory.setFilterRules("qt.webenginecontext.debug=true")
 
     s = QWebEngineProfile.defaultProfile().settings()
-    s.setAttribute(QWebEngineSettings.PluginsEnabled, True)
-    s.setAttribute(QWebEngineSettings.DnsPrefetchEnabled, True)
+    s.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
+    s.setAttribute(QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True)
 
     browser = Browser()
     window = browser.create_hidden_window()
index bbf5004f5a36b01321a5026e075480aa8ddbfc29..623dea571a7d0ef332c164eabf03d0ca87000cc9 100644 (file)
@@ -20,7 +20,7 @@
       <string>Icon</string>
      </property>
      <property name="alignment">
-      <set>Qt::AlignCenter</set>
+      <set>Qt::AlignmentFlag::AlignCenter</set>
      </property>
     </widget>
    </item>
    <item row="2" column="1">
     <widget class="QLineEdit" name="m_passwordLineEdit">
      <property name="echoMode">
-      <enum>QLineEdit::Password</enum>
+      <enum>QLineEdit::EchoMode::Password</enum>
      </property>
     </widget>
    </item>
    <item row="3" column="0" colspan="2">
     <widget class="QDialogButtonBox" name="buttonBox">
      <property name="orientation">
-      <enum>Qt::Horizontal</enum>
+      <enum>Qt::Orientation::Horizontal</enum>
      </property>
      <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
      </property>
     </widget>
    </item>
index bda321ac1d33d8042fbbc2a409579355f27b58ba..4a5a933bb234bbec0a6515cefbc89688f440e19f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from functools import partial
 
@@ -27,14 +28,14 @@ class TabWidget(QTabWidget):
         self._profile = profile
         tab_bar = self.tabBar()
         tab_bar.setTabsClosable(True)
-        tab_bar.setSelectionBehaviorOnRemove(QTabBar.SelectPreviousTab)
+        tab_bar.setSelectionBehaviorOnRemove(QTabBar.SelectionBehavior.SelectPreviousTab)
         tab_bar.setMovable(True)
-        tab_bar.setContextMenuPolicy(Qt.CustomContextMenu)
+        tab_bar.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
         tab_bar.customContextMenuRequested.connect(self.handle_context_menu_requested)
         tab_bar.tabCloseRequested.connect(self.close_tab)
         tab_bar.tabBarDoubleClicked.connect(self._tabbar_double_clicked)
         self.setDocumentMode(True)
-        self.setElideMode(Qt.ElideRight)
+        self.setElideMode(Qt.TextElideMode.ElideRight)
 
         self.currentChanged.connect(self.handle_current_changed)
 
@@ -59,14 +60,14 @@ class TabWidget(QTabWidget):
             self.load_progress.emit(view.load_progress())
             self.url_changed.emit(view.url())
             self.fav_icon_changed.emit(view.fav_icon())
-            e = view.is_web_action_enabled(QWebEnginePage.Back)
-            self.web_action_enabled_changed.emit(QWebEnginePage.Back, e)
-            e = view.is_web_action_enabled(QWebEnginePage.Forward)
-            self.web_action_enabled_changed.emit(QWebEnginePage.Forward, e)
-            e = view.is_web_action_enabled(QWebEnginePage.Stop)
-            self.web_action_enabled_changed.emit(QWebEnginePage.Stop, e)
-            e = view.is_web_action_enabled(QWebEnginePage.Reload)
-            self.web_action_enabled_changed.emit(QWebEnginePage.Reload, e)
+            e = view.is_web_action_enabled(QWebEnginePage.WebAction.Back)
+            self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Back, e)
+            e = view.is_web_action_enabled(QWebEnginePage.WebAction.Forward)
+            self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Forward, e)
+            e = view.is_web_action_enabled(QWebEnginePage.WebAction.Stop)
+            self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Stop, e)
+            e = view.is_web_action_enabled(QWebEnginePage.WebAction.Reload)
+            self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Reload, e)
         else:
             self.title_changed.emit("")
             self.load_progress.emit(0)
index a963f0ac0dba7a3c8cad3d80f38431df0243aef6..3bbdcacbbf41d604850e1e140440d8ab513a9cf2 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'certificateerrordialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -23,13 +23,13 @@ class Ui_CertificateErrorDialog(object):
     def setupUi(self, CertificateErrorDialog):
         if not CertificateErrorDialog.objectName():
             CertificateErrorDialog.setObjectName(u"CertificateErrorDialog")
-        CertificateErrorDialog.resize(370, 141)
+        CertificateErrorDialog.resize(689, 204)
         self.verticalLayout = QVBoxLayout(CertificateErrorDialog)
         self.verticalLayout.setObjectName(u"verticalLayout")
         self.verticalLayout.setContentsMargins(20, -1, 20, -1)
         self.m_iconLabel = QLabel(CertificateErrorDialog)
         self.m_iconLabel.setObjectName(u"m_iconLabel")
-        self.m_iconLabel.setAlignment(Qt.AlignCenter)
+        self.m_iconLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
 
         self.verticalLayout.addWidget(self.m_iconLabel)
 
@@ -40,7 +40,7 @@ class Ui_CertificateErrorDialog(object):
         sizePolicy.setVerticalStretch(0)
         sizePolicy.setHeightForWidth(self.m_errorLabel.sizePolicy().hasHeightForWidth())
         self.m_errorLabel.setSizePolicy(sizePolicy)
-        self.m_errorLabel.setAlignment(Qt.AlignCenter)
+        self.m_errorLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
         self.m_errorLabel.setWordWrap(True)
 
         self.verticalLayout.addWidget(self.m_errorLabel)
@@ -52,7 +52,7 @@ class Ui_CertificateErrorDialog(object):
         sizePolicy1.setVerticalStretch(0)
         sizePolicy1.setHeightForWidth(self.m_infoLabel.sizePolicy().hasHeightForWidth())
         self.m_infoLabel.setSizePolicy(sizePolicy1)
-        self.m_infoLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
+        self.m_infoLabel.setAlignment(Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter)
         self.m_infoLabel.setWordWrap(True)
 
         self.verticalLayout.addWidget(self.m_infoLabel)
@@ -63,8 +63,8 @@ class Ui_CertificateErrorDialog(object):
 
         self.buttonBox = QDialogButtonBox(CertificateErrorDialog)
         self.buttonBox.setObjectName(u"buttonBox")
-        self.buttonBox.setOrientation(Qt.Horizontal)
-        self.buttonBox.setStandardButtons(QDialogButtonBox.No|QDialogButtonBox.Yes)
+        self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+        self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.No|QDialogButtonBox.StandardButton.Yes)
 
         self.verticalLayout.addWidget(self.buttonBox)
 
index f0f61aa75aaf22f341aef03df5ee71bda7903802..51f46e087d5da0183c6988a448d2174f519ccf5e 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'downloadmanagerwidget.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -28,7 +28,7 @@ class Ui_DownloadManagerWidget(object):
 "}")
         self.m_topLevelLayout = QVBoxLayout(DownloadManagerWidget)
         self.m_topLevelLayout.setObjectName(u"m_topLevelLayout")
-        self.m_topLevelLayout.setSizeConstraint(QLayout.SetNoConstraint)
+        self.m_topLevelLayout.setSizeConstraint(QLayout.SizeConstraint.SetNoConstraint)
         self.m_topLevelLayout.setContentsMargins(0, 0, 0, 0)
         self.m_scrollArea = QScrollArea(DownloadManagerWidget)
         self.m_scrollArea.setObjectName(u"m_scrollArea")
@@ -36,12 +36,13 @@ class Ui_DownloadManagerWidget(object):
 "  margin: 2px;\n"
 "  border: none;\n"
 "}")
-        self.m_scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
-        self.m_scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+        self.m_scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
+        self.m_scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
         self.m_scrollArea.setWidgetResizable(True)
-        self.m_scrollArea.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
+        self.m_scrollArea.setAlignment(Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignTop)
         self.m_items = QWidget()
         self.m_items.setObjectName(u"m_items")
+        self.m_items.setGeometry(QRect(0, 0, 382, 208))
         self.m_items.setStyleSheet(u"#m_items {background: palette(mid)}")
         self.m_itemsLayout = QVBoxLayout(self.m_items)
         self.m_itemsLayout.setSpacing(2)
@@ -55,7 +56,7 @@ class Ui_DownloadManagerWidget(object):
         sizePolicy.setHeightForWidth(self.m_zeroItemsLabel.sizePolicy().hasHeightForWidth())
         self.m_zeroItemsLabel.setSizePolicy(sizePolicy)
         self.m_zeroItemsLabel.setStyleSheet(u"color: palette(shadow)")
-        self.m_zeroItemsLabel.setAlignment(Qt.AlignCenter)
+        self.m_zeroItemsLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
 
         self.m_itemsLayout.addWidget(self.m_zeroItemsLabel)
 
index 58c32fdf88b2bf45836e141918060a6adedda28d..5204b68854fc82051de801a03e5c469353dfe722 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'downloadwidget.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -23,6 +23,7 @@ class Ui_DownloadWidget(object):
     def setupUi(self, DownloadWidget):
         if not DownloadWidget.objectName():
             DownloadWidget.setObjectName(u"DownloadWidget")
+        DownloadWidget.resize(144, 103)
         DownloadWidget.setStyleSheet(u"#DownloadWidget {\n"
 "  background: palette(button);\n"
 "  border: 1px solid palette(dark);\n"
@@ -30,7 +31,7 @@ class Ui_DownloadWidget(object):
 "}")
         self.m_topLevelLayout = QGridLayout(DownloadWidget)
         self.m_topLevelLayout.setObjectName(u"m_topLevelLayout")
-        self.m_topLevelLayout.setSizeConstraint(QLayout.SetMinAndMaxSize)
+        self.m_topLevelLayout.setSizeConstraint(QLayout.SizeConstraint.SetMinAndMaxSize)
         self.m_dstName = QLabel(DownloadWidget)
         self.m_dstName.setObjectName(u"m_dstName")
         self.m_dstName.setStyleSheet(u"font-weight: bold\n"
index 11e0c4a2e0acec2dc67881d07cedd2658067124e..05bec7b538c8e06b830032b45f2abf90273a9a1d 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'passworddialog.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -28,7 +28,7 @@ class Ui_PasswordDialog(object):
         self.gridLayout.setObjectName(u"gridLayout")
         self.m_iconLabel = QLabel(PasswordDialog)
         self.m_iconLabel.setObjectName(u"m_iconLabel")
-        self.m_iconLabel.setAlignment(Qt.AlignCenter)
+        self.m_iconLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
 
         self.gridLayout.addWidget(self.m_iconLabel, 0, 0, 1, 1)
 
@@ -60,14 +60,14 @@ class Ui_PasswordDialog(object):
 
         self.m_passwordLineEdit = QLineEdit(PasswordDialog)
         self.m_passwordLineEdit.setObjectName(u"m_passwordLineEdit")
-        self.m_passwordLineEdit.setEchoMode(QLineEdit.Password)
+        self.m_passwordLineEdit.setEchoMode(QLineEdit.EchoMode.Password)
 
         self.gridLayout.addWidget(self.m_passwordLineEdit, 2, 1, 1, 1)
 
         self.buttonBox = QDialogButtonBox(PasswordDialog)
         self.buttonBox.setObjectName(u"buttonBox")
-        self.buttonBox.setOrientation(Qt.Horizontal)
-        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+        self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+        self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
 
         self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2)
 
index 2f2800a172cf8e1880eecb2f79b459b3d03dc2e4..e5d9bc6ab140e062df50330cb3b43b1373dbc76a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from functools import partial
 
index fac27a61a9dd4164f864aed30c5db7e8f5c0d6c9..fd82ed9f25c507e2bb0f1f8a81eed926ed6ef8db 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QLineEdit, QSizePolicy, QWidget, QVBoxLayout
 from PySide6.QtGui import QAction
@@ -17,8 +18,8 @@ class WebPopupWindow(QWidget):
         self._fav_action = QAction(self)
         self._view = view
 
-        self.setAttribute(Qt.WA_DeleteOnClose)
-        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
+        self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
+        self.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
 
         layout = QVBoxLayout(self)
         layout.setContentsMargins(0, 0, 0, 0)
@@ -29,7 +30,7 @@ class WebPopupWindow(QWidget):
         self._view.setFocus()
 
         self._url_line_edit.setReadOnly(True)
-        self._url_line_edit.addAction(self._fav_action, QLineEdit.LeadingPosition)
+        self._url_line_edit.addAction(self._fav_action, QLineEdit.ActionPosition.LeadingPosition)
 
         self._view.titleChanged.connect(self.setWindowTitle)
         self._view.urlChanged.connect(self._url_changed)
index e1282c1dd6e5a67c03f5cd5b93f24c49b6f82c98..c7e577176b76900eeeccc684ff3954225b69f375 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from functools import partial
 
@@ -106,10 +107,10 @@ class WebView(QWebEngineView):
                 self.handle_register_protocol_handler_requested)
             old_page.fileSystemAccessRequested.disconnect(self.handle_file_system_access_requested)
 
-        self.create_web_action_trigger(page, QWebEnginePage.Forward)
-        self.create_web_action_trigger(page, QWebEnginePage.Back)
-        self.create_web_action_trigger(page, QWebEnginePage.Reload)
-        self.create_web_action_trigger(page, QWebEnginePage.Stop)
+        self.create_web_action_trigger(page, QWebEnginePage.WebAction.Forward)
+        self.create_web_action_trigger(page, QWebEnginePage.WebAction.Back)
+        self.create_web_action_trigger(page, QWebEnginePage.WebAction.Reload)
+        self.create_web_action_trigger(page, QWebEnginePage.WebAction.Stop)
         super().setPage(page)
         page.create_certificate_error_dialog.connect(self.handle_certificate_error)
         page.authenticationRequired.connect(self.handle_authentication_required)
index 2db865996095ac15cd8c5fafd9f117261f927218..b8477112ae0e6cfcb9591a297c14bc96ec3071b4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 WebEngineWidgets Example"""
 
diff --git a/examples/webview/minibrowser/doc/minibrowser.rst b/examples/webview/minibrowser/doc/minibrowser.rst
new file mode 100644 (file)
index 0000000..334b95b
--- /dev/null
@@ -0,0 +1,8 @@
+Minibrowser Example
+===================
+
+Simple application that demonstrates how to use a QWebView modules with Qt Quick.
+
+.. image:: minibrowser.webp
+   :width: 800
+   :alt: Minibrowser screenshot
diff --git a/examples/webview/minibrowser/doc/minibrowser.webp b/examples/webview/minibrowser/doc/minibrowser.webp
new file mode 100644 (file)
index 0000000..b7aca89
Binary files /dev/null and b/examples/webview/minibrowser/doc/minibrowser.webp differ
diff --git a/examples/webview/minibrowser/images/left-32.png b/examples/webview/minibrowser/images/left-32.png
new file mode 100644 (file)
index 0000000..28e4dda
Binary files /dev/null and b/examples/webview/minibrowser/images/left-32.png differ
diff --git a/examples/webview/minibrowser/images/refresh-32.png b/examples/webview/minibrowser/images/refresh-32.png
new file mode 100644 (file)
index 0000000..886cad4
Binary files /dev/null and b/examples/webview/minibrowser/images/refresh-32.png differ
diff --git a/examples/webview/minibrowser/images/right-32.png b/examples/webview/minibrowser/images/right-32.png
new file mode 100644 (file)
index 0000000..00ccf43
Binary files /dev/null and b/examples/webview/minibrowser/images/right-32.png differ
diff --git a/examples/webview/minibrowser/images/settings-32.png b/examples/webview/minibrowser/images/settings-32.png
new file mode 100644 (file)
index 0000000..948d90e
Binary files /dev/null and b/examples/webview/minibrowser/images/settings-32.png differ
diff --git a/examples/webview/minibrowser/images/stop-32.png b/examples/webview/minibrowser/images/stop-32.png
new file mode 100644 (file)
index 0000000..3f5fb8b
Binary files /dev/null and b/examples/webview/minibrowser/images/stop-32.png differ
diff --git a/examples/webview/minibrowser/main.py b/examples/webview/minibrowser/main.py
new file mode 100644 (file)
index 0000000..24ca3e8
--- /dev/null
@@ -0,0 +1,60 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtCore import QCoreApplication, QUrl, QRect, QPoint
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtWebView import QtWebView
+import argparse
+
+import rc_qml  # noqa: F401
+
+
+class Utils:
+    @staticmethod
+    def fromUserInput(userInput):
+        if not userInput:
+            return QUrl.fromUserInput("about:blank")
+        result = QUrl.fromUserInput(userInput)
+        return result if result.isValid() else QUrl.fromUserInput("about:blank")
+
+
+if __name__ == "__main__":
+    QtWebView.initialize()
+    app = QGuiApplication(sys.argv)
+    QGuiApplication.setApplicationDisplayName(QCoreApplication
+                                              .translate("main", "QtWebView Example"))
+
+    parser = argparse.ArgumentParser(description=QGuiApplication.applicationDisplayName())
+    parser.add_argument("--url", nargs="?",
+                        default="https://www.qt.io",
+                        help="The initial URL to open.")
+    args = parser.parse_args()
+    initialUrl = args.url
+
+    engine = QQmlApplicationEngine()
+    context = engine.rootContext()
+    context.setContextProperty("utils", Utils())
+    context.setContextProperty("initialUrl", Utils.fromUserInput(initialUrl))
+
+    geometry = QGuiApplication.primaryScreen().availableGeometry()
+    if not QGuiApplication.styleHints().showIsFullScreen():
+        size = geometry.size() * 4 / 5
+        offset = (geometry.size() - size) / 2
+        pos = geometry.topLeft() + QPoint(offset.width(), offset.height())
+        geometry = QRect(pos, size)
+
+    engine.setInitialProperties({"x": geometry.x(), "y": geometry.y(),
+                                 "width": geometry.width(), "height": geometry.height()})
+    qml_file = Path(__file__).parent / "main.qml"
+    engine.load(QUrl.fromLocalFile(qml_file))
+
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
diff --git a/examples/webview/minibrowser/main.qml b/examples/webview/minibrowser/main.qml
new file mode 100644 (file)
index 0000000..f647c62
--- /dev/null
@@ -0,0 +1,153 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtWebView
+import QtQuick.Layouts
+
+
+ApplicationWindow {
+    id: window
+    visible: true
+    title: webView.title
+
+    menuBar: ToolBar {
+        id: navigationBar
+        RowLayout {
+            anchors.fill: parent
+            spacing: 0
+
+            ToolButton {
+                id: backButton
+                icon.source: "qrc:/left-32.png"
+                onClicked: webView.goBack()
+                enabled: webView.canGoBack
+                Layout.preferredWidth: navigationBar.height
+            }
+
+            ToolButton {
+                id: forwardButton
+                icon.source: "qrc:/right-32.png"
+                onClicked: webView.goForward()
+                enabled: webView.canGoForward
+                Layout.preferredWidth: navigationBar.height
+            }
+
+            ToolButton {
+                id: reloadButton
+                icon.source: webView.loading ? "qrc:/stop-32.png" : "qrc:/refresh-32.png"
+                onClicked: webView.loading ? webView.stop() : webView.reload()
+                Layout.preferredWidth: navigationBar.height
+            }
+
+            Item { Layout.preferredWidth: 5 }
+
+            TextField {
+                Layout.fillWidth: true
+                id: urlField
+                inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhPreferLowercase
+                text: webView.url
+                onAccepted: webView.url = utils.fromUserInput(text)
+             }
+
+            Item { Layout.preferredWidth: 5 }
+
+            ToolButton {
+                id: goButton
+                text: qsTr("Go")
+                onClicked: {
+                    Qt.inputMethod.commit()
+                    Qt.inputMethod.hide()
+                    webView.url = utils.fromUserInput(urlField.text)
+                }
+            }
+
+            ToolButton {
+                id: settingsButton
+                icon.source: "qrc:/settings-32.png"
+                onClicked: {
+                    settingsDrawer.width = (settingsDrawer.width > 0) ? 0 : window.width * 1/4
+                }
+                Layout.preferredWidth: navigationBar.height
+            }
+
+            Item { Layout.preferredWidth: 10 }
+         }
+         ProgressBar {
+             id: progress
+             anchors {
+                left: parent.left
+                top: parent.bottom
+                right: parent.right
+                leftMargin: parent.leftMargin
+                rightMargin: parent.rightMargin
+             }
+             height:3
+             z: Qt.platform.os === "android" ? -1 : -2
+             background: Item {}
+             visible: Qt.platform.os !== "ios" && Qt.platform.os !== "winrt"
+             from: 0
+             to: 100
+             value: webView.loadProgress < 100 ? webView.loadProgress : 0
+        }
+    }
+
+    Item {
+        id: settingsDrawer
+        anchors.right: parent.right
+        ColumnLayout {
+            Label {
+                text: "JavaScript"
+            }
+            CheckBox {
+                id: javaScriptEnabledCheckBox
+                text: "enabled"
+                onCheckStateChanged: webView.settings.javaScriptEnabled = (checkState == Qt.Checked)
+            }
+            Label {
+                text: "Local storage"
+            }
+            CheckBox {
+                id: localStorageEnabledCheckBox
+                text: "enabled"
+                onCheckStateChanged: webView.settings.localStorageEnabled = (checkState == Qt.Checked)
+            }
+            Label {
+                text: "Allow file access"
+            }
+            CheckBox {
+                id: allowFileAccessEnabledCheckBox
+                text: "enabled"
+                onCheckStateChanged: webView.settings.allowFileAccess = (checkState == Qt.Checked)
+            }
+            Label {
+                text: "Local content can access file URLs"
+            }
+            CheckBox {
+                id: localContentCanAccessFileUrlsEnabledCheckBox
+                text: "enabled"
+                onCheckStateChanged: webView.settings.localContentCanAccessFileUrls = (checkState == Qt.Checked)
+            }
+        }
+    }
+
+    WebView {
+        id: webView
+        url: initialUrl
+        anchors.right: settingsDrawer.left
+        anchors.left: parent.left
+        height: parent.height
+        onLoadingChanged: function(loadRequest) {
+            if (loadRequest.errorString)
+                console.error(loadRequest.errorString);
+        }
+
+        Component.onCompleted: {
+            javaScriptEnabledCheckBox.checkState = settings.javaScriptEnabled ? Qt.Checked : Qt.Unchecked
+            localStorageEnabledCheckBox.checkState = settings.localStorageEnabled ? Qt.Checked : Qt.Unchecked
+            allowFileAccessEnabledCheckBox.checkState = settings.allowFileAccess ? Qt.Checked : Qt.Unchecked
+            localContentCanAccessFileUrlsEnabledCheckBox.checkState = settings.localContentCanAccessFileUrls ? Qt.Checked : Qt.Unchecked
+        }
+    }
+}
diff --git a/examples/webview/minibrowser/minibrowser.pyproject b/examples/webview/minibrowser/minibrowser.pyproject
new file mode 100644 (file)
index 0000000..4661706
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "files": ["main.py", "main.qml", "qml.qrc", "images/left-32.png", "images/right-32.png",
+              "images/refresh-32.png", "images/settings-32.png", "images/stop-32.png"]
+}
diff --git a/examples/webview/minibrowser/qml.qrc b/examples/webview/minibrowser/qml.qrc
new file mode 100644 (file)
index 0000000..f360471
--- /dev/null
@@ -0,0 +1,9 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+        <file alias="left-32.png">images/left-32.png</file>
+        <file alias="stop-32.png">images/stop-32.png</file>
+        <file alias="refresh-32.png">images/refresh-32.png</file>
+        <file alias="right-32.png">images/right-32.png</file>
+    </qresource>
+</RCC>
diff --git a/examples/webview/minibrowser/rc_qml.py b/examples/webview/minibrowser/rc_qml.py
new file mode 100644 (file)
index 0000000..104eadb
--- /dev/null
@@ -0,0 +1,368 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.7.2
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x02\x7f\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00i\x00\xa1\x006za\
+\x0c\x8d\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x1a\x09+7\xecd\xf9\xf8\x00\x00\x02\x0cID\
+ATX\xc3\xcd\x97\xb1K#A\x14\xc6\xbfy\xbb\x90\
+\x85\xa4\xb0\x91\x03\x8b\x88WX\xdc),\xd8X\xad6\
+f\x09X\xab\x95r\x049\xb0\xd2\x08\xfe\x11\xa2I\xe0\
+@\x10\x11\xb1\x10\xf4\x8a\x03+1\x07G\xd8\xea@\x02\
+)\xbc+,\x84\xa4\x10\xd16\xc2.$3\x16\xee\x84\
+\xf5\xdcpF7\x93L\xb9;\xcb\xef{\xdf\xbc\xf7\xf6\
+\x0d\xd0\xe3\xc5:\xd9\xbc\xbff\x99\x86\xc7\x96\x12\x0f\x94\
+NT\x1b\xa3h\xfa\xdfk\x10\xf5a\xfd\xaa\x1e\xe7g\
+nL\x1cf\xf2N%2\x01{\xeb\x16\x19\x1e\xcb\x0e\
+]\xf2\xcd\x16\xf0\x7fK\x83\xb8\x19\xa3\x0d7&r\xcb\
+\xdb\x0e\x7f\xb3\x80\x83Uk&Y\x11\xe7\xaf\x06\x87\x08\
+\xa9\x99\xcc\xfeRp~\xb6\xdbB\xed^\x1c\xadL\xe5\
+\x93eQ|3\x1c\x00\x9a`\xc9\xb2(\x1e\xadL\xe5\
+;r\xe0\xf8\xeb\xf4\xc9\xe0\xdf\xe6\x5c\x94\xc9v\xffI\
+\xfb\xbe\xb0[\x9a\x7fiRH\xe4\x1f\xfe\xf0L\xd4\xd9\
+\x1e\xbf\x17\x9f\xed\xd9\x91\x81\x1f\x17\xd5\xf3\xb6\x0e\x1c\xac\
+Z3\xc9\xb2(v\xb3\xecj\x13,\x15\xcc\x09\x16\xcc\
+\xf6\x8fe\xd1x\xd7\x99\xbf21\xaf'\x98.\xabC\
+\x97\xcf\x0d\x8fe\xd1\x14\xa1p2\x0cL\x17;3\xa6\
+\x94J\x81\xbbnhb\x1a\x1e\xcb\x02\xd8zV\x05C\
+\x97|SU\xf7\x0b\xb2Hv\xb8\xae[\xff\x8f\x0b\xfb\
+k\x96\xd9\x12`xlI\xf5?@2\x09\x00\x12\x0f\
+\x94V-@2\x9f\x04T\x1b\xa3\xca\x05\xf8L\x92g\
+\xa2\xfc?\xec3\xa9\xd7\xf3\x00\xc9\xe6\xa0\x9c\xec3\x09\
+\x00\xea\xc3\xfa\x95j\xbed>\x09\x88\xf33\xe5\x02|\
+&\x01\x80\x1b\x13\x87\xaa\x05H&\x01@&\xefT\x94\
+\xe6\x81\x06!\xe7\xc6V\x15\xdc\x8c\xd1\x86*~\x90\xa5\
+\x07,\xc9AC\xe8\xe0\xc9]\x17\xbf,+\xb2\xe8\xdd\
+\x98\xc8\xbd\x98\x09\x97\xb7\x1d^3\x99\xdd\xed\xe8k&\
+\xb3\x83\x93\xf2\xb3\x91\xec\xf4w\xf5\xda\x9e\x1d\x19H\xdc\
+\x89\xc9n\xc0o\xc7\xa9\xb0\xf8\xcd\xd9\xe9\xab\xa14\xb4\
+\x15/\xec\x96\xe6o\xc7\xa9\x10e\xe4a\xf0\xbe\xb8\x98\
+\xf4\xf7\xd5L\xc5\xe5\xb4\xe7\xeb\x11\x07R\xed#?\x12\
+G\x0e\x00\x00\x00\x00IEND\xaeB`\x82\
+\x00\x00\x05\x15\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00\xd9\x00M\x00M\x0d\x89\
+\x85\xe9\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x17\x0f!*\x83j\xd9\xc4\x00\x00\x04\xa2ID\
+ATX\xc3\xcd\x97[L\x93g\x18\xc7\x7f_)\x05\
+Z\x0a\x149\xaa\x84\x83\xe0&\xa2\x11g\xc0L\xdc\x92\
+ZL\x06\xd9\xc6\x95\xcb\x8e\xbd\xf1b\xa0u\xa7\xbb\x1d\
+b\xdc\xc5\xee\xb69\xaa\xf4\xc6,a\xd9\x5cf\xb2\xc4\
+]\xa8\x09\x87&\x1b\x90(q\xe0\x1c\xe2\x06(8\x0f\
+\x9c\x84ZX\x81r\xe8\xbb\x0b\xfbu\xdf\xd7\x16\xf8\x98\
+K\xf4M\x9a4\xcf\xfb\xbc\xcf\xff\xff>\x87\xf7y>\
+x\xccKZ\x8brmcY6\xb0\x17\xa8\x04*\x80\
+\xbc\xe0\xd6\x10\xd0\x0e4\x03m.{\xe7\xf0\xffF\xa0\
+\xb6\xb1L\x02\xac@=P\xac\xd1n/p\x04p\xbb\
+\xec\x9d\xe2?\x13\xa8m,+\x02\xce\xae\x018\x1a\x91\
+\x1a\x97\xbd\xb3\x7f9\x05\xdd\x0a\xe0\x07\x80\xbeG\x00'\
+x\xb6/hK\xbb\x07j\x1b\xcb\x0e\x01'\xc2\xe5\xa9\
+\xa6,Js\xad\x14e\xee$;9\x1fS\x5c\x0a\x82\
+\x00\xd3s\x1e\xeez\x06\xb8~\xef\x22]C-\xcc-\
+\xceD3{\xd8e\xef<\xb9*\x81 \xdb\x1f\x94\xb2\
+\xac\xe4<^.\xad\xa3dc\x05:)\xd2i\xb3\xf3\
+>\xbc\xb3\xe3H\x92\x8e\xf8X\x13?\xffq\x86\x96\xde\
+\xefX\x0a,\x86\xab\xbe\xe2\xb2w\x9eY\x96@0\xe6\
+}\xffnJT\x96\xbcE\xf5\xf6\x83\xc4\xea\x0d*K\
+\xfe\x85Y\xda\xfb\xcfr\xf1\xc69\xeey\x06\x10<\xcc\
+5\x9d\x14C~\xfa66Z\x8a\xb8<\xd4\x84\xcf\xef\
+\x0d'\xb1Y\x99\x13RX\xb6\xf7\xc81\x97\x90x\xf3\
+\xd9O\xd8]X\x0d\x80\x10\x02I\x92\x10B08\xde\
+\xc3\xd7m\x1f3\xe9\x1bY1\x01L\x86d|\xf3\xde\
+h\x89Y\x22W\x87\xd2\x9fVe\xc2\xd5\xec<L\xf9\
+\xa6*\x84\x10\x08!B$\xfaF~\xe5xS\xdd\xaa\
+\xe0@4p91\xad\xd1\xaa\xa0^\xfe\xb3)c\x07\
+\xd6\xe2W\x91$I\xf5\xf3\xceNp\xea\x97\x0fY\x0c\
+\xcck.\x03\x83>!\x9a\xb8^E \xf8\xc2\x85n\
+\xffR\xe9\xdb\xc4\xe8bB7\x97o\x7f\xfe\xb7S\xd1\
+b\xba\xec*H\xdf\xce\xa75?\x92\x9f\xb6-\xc2\x0b\
+A\xcc\x90\x07\xf6\xca;\x99I\xb9\x14f\xecP\xb9]\
+\x08\xc1\xfc\xe2\x1c\x97n\x9e\xd7\x0c\x9e\x93\xfa\x14\xb5\xd6\
+\xcf1'\xa4R\xb7\xef\x0b6X\x8a\xc2U\xf6*\x09\
+T\x86R4kWT\x837\xc7\xaf\xb2\xb0\xe4\xd7\x04\
+\x9e\x99\x94\xcb!\xeb\x97\x18\x0df\x00\x8c\x063\x8e}\
+_\x91n\xceQ\xaaU*\x09T\xc8\xd2\x8c\xa4\x9c\x88\
+\xd8K\x92\xc4\xf8\xf4\x1dM\xe0\xa9\xa6,\x1c6'I\
+\xc6u\xaa\xf3I\xc6u\x1c\xa9tb1f\xa2\xc4\x94\
+\x09\xe4\xed\xdb\xf2\x1a\xf6=G)\xd9\xb0G\xe5~y\
+\x15f\x94b\xdfs\x14[\xf1\x1b\xcb\x82\x9b\xe3Sq\
+\xd8\x9cXL\x19\xaa\xea\x91m\xadK\xcc\xc6Q\xe9$\
+1\xce\x82\xdcICUp\xe9\xe6\x05r\xd3\xb6\x92\x91\
+\x94\xa3\x02\x97\x0de\xa7\xe4\x93\x97V\xb2b\x1e\x94\x17\
+T1t\xbf\x87\xe1\x07\x83*`\xd9\xd6\xa8\xf7/n\
+\xdd\xbfFy\xc1\x0b\x11\xcdh\xe8o\xbf\x07g\xb3\x83\
+I\xdfH\xd4\x10x|c8[\x1cL\xcfM.K\
+\xa0\xa5\xf7[\x1a;\x8e10\xd6\x1d\xd5F\xcf\xdd\x0e\
+\x1a;\x8e\xd1z\xfd\xb4<C\x84\x08\xb4\x03xfF\
+\xa9ov053\xa1\xf2\xc2\xd4\xcc\x04\xce\x16\x87\xa6\
+\xc7\x07 \xdd\xbc1B&\x84`l\xea\xb6R\xd4\xae\
+$\xd0,K\xc7\xa7o\xe3l}\x07\x9f\x7f\xea\xe1k\
+\xe6\x9f\xe2\xa4\xfb=F\xa7ni\x02\x8f\x8d\x89\xa3 \
+}{\xc8\xf5\xca\x10\xf4\x8d\x5cV\xaa6+\x09\xb4)\
+w\xeez\xfaih}\x9f\xe9\xd9I\x5c\xee\x0f\xb8=\
+\xf9\xa7\xe6\xfa//\xa8\xc2\xa0\x8f\x8f\x90\x0f\x8c]\x09\
+\xbfD[x3\xba\x16>|\x18\xf4\x09\xcc/\xcej\
+\x067\xc5%\xf3\xd1\x8b\xa7I1\xa6\xa9\xe4K\x81%\
+\x8e7\xd5qc\xecJ\xa8!\xb9\xec\x9d[\xc3{\xc1\
+\x91p\x83k\x01\xd7\xeb\x0c\x1c|\xee3R\x8ci\x11\
+\xaew\xf7~\xaf\x04Wa)\x09\xb8\x83\xad2\xa2\xa5\
+jy|\xde\xdd\xdf\xc0\xe6\xacgT\xb5\x0fpq\xe0\
+\x1cg\xbbN\x84\xb7c\xb7\xa6\x81Dv\xeb\xae\xbc\xfd\
+\xdc\xf1\xf438\xfe;\x01\xb1\x14\x1aV\xd6[\x0a\xd9\
+\xbd\xa9\x9a\x8a\xa2\x1a\xe2b\x13T3\xc3\xe2\xd2\x02\xe7\
+\xae\x9e\xa2\xb9\xe7\x9b\xd0\xb0\xb2\xe2@\xb2\xd2H\x16\xa3\
+\xd3c+~\x9d\xe7\x9f>\xc0\xdc\x82\x0f!\x02$'\
+\xa4c\x8cK\x8c(\xb5\x80\x08\xd0s\xa7\x9d\x9f\xba\x1b\
+\x18\xf1\x0e\xadm$[m(\x8d\xd7\x1b\xd9\x99gc\
+\xcb\xfa\xddl\xb0\x14b\x8e\xb7 \xa1\xc3\xe7\x7f\xc0\xb0\
+w\x90\xfe\xd1.\xbao\xb9\x97{/\xb4\x0d\xa5+y\
+\xe2\x11V\xc4\xcd\x9f\xfc\x0f\x13\x80\xe0\xc1\x12\xc0\x16\xad\
+BV\x01\xb6\x05\x87\xcf\xfe'\xfa\xe3\xf4\xb1\xaf\x7f\x00\
+\x9e\xe8\x03h^\xfe7\xb1\x00\x00\x00\x00IEND\
+\xaeB`\x82\
+\x00\x00\x03?\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00i\x00\xa1\x006za\
+\x0c\x8d\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x17\x0f)\x18\x83d\x02L\x00\x00\x02\xccID\
+ATX\xc3\xcd\x97\xcfKTQ\x14\xc7?o&\x89\
+\x22P\xc8~)\xd9\xd0/$tF\x03g\xe5d\x98\
+#I?\xd0\x8d\xd2\x22'\xc5\xa2A\xe9\x0f\xe8\xc7\xa6\
+ h;\x10\x13\x96\xc6\xe0J7\x0dd\xb8\x18\x1c\xc4\
+\xb1\xcdc\xcaQ$r\x5c\xe4 \xa6\x1b\x17B\xe0F\
+}-z\xef\xf1\xe6\xbd\xf9\xf1\xde\xa8\xe9Y\xbdw\xee\
+\xb9\xf7\xfb\xe5\x9c\xef\xbd\xf7\x5c\xd8g\x13\xac\x04\xfbC\
+\xee3\x80\x07\xf0\x02\xf5\x80C\x1eZ\x04\xa6\x80\x08\x10\
+\x0b\xfa\xc4\x95]#\xe0\x0f\xb9\x05\xa0\x11\x08\x00WL\
+\xae\xfb\x03x\x02D\x83>Q*\x98\x80?\xe4\xbe\x04\
+\x84-\x00g\x22\xd2\x1a\xf4\x89\x0b\xd9\x02l9\xc0\xdb\
+\x81\xe4\x0e\xc0\x91\xe7&\xe5\xb5\xcc\x13\xf0\x87\xdc\xbd\xc0\
+\xf0.jmX^3\x7f\x09d\xb6\xc3{$\xfa\x8e\
+\xa0O\x1c\xc9J@\xaey2\xdf*\xd7+\xdb)=\
+Vn\xf0\xcf-\x7f\xe5\xe7\x8a\x98o\xfae\xad&\x04\
+\x9d\xda\xe7\xf2\xd5\xbc\xb5\xb6\x17o\xd5}\x04!=y\
+\xd3\xa9(\x03\x93\xcf\xd9\x96\xb6\xcc\x08\xb3J\xd9\x1dZ\
+\x0d4\x9a\x01o\xae\xee4\x80'R\x13\x0c\xc6^\x98\
+\x01W\x84\xd9\x98I\x84\x81\x5c\xb3\xee\xd6\xfai\xae\xee\
+4\xf8\x13\xa9\x09\x06b\xcf\xd8\xda\xde\xb4\xa2\x85@Z\
+\x09\xe4\x13\xeew\xb6\xe8;5\x8f\xb9Y\xfd\xc0\xe0\x9f\
+]\x9a\xe4\xc3\xe4S\xab\xe0\x8a\x95\x05}\xe2\x8a\x92\x01\
+\xcf\x7f\x06W1\x0f\xc9?\xdeL\x11\xb7]\x8fTp\
+A\x10\x90\xa4\x7f\xa7\xea\xfa\xc6\x1a\xdfS\xe38\xcf6\
+XB\x9cN\x8dk\x7f\xbd\xc0\x88B\xa0>\x13x\x8b\
+\xb3;Mp\xcaw\xc9\xd1R\xba</-\x81K\x92\
+D\xefP\x1a\x81zm\x06\x1c\xda\x91[\xae\x87\xb48\
+\xbb\xd5\x89\xfa\x0c\xe8\xffs\x8di\x89\xeb\xcc\xa1%P\
+\xd8].\x08;>\x1am\x9a\xfb\x5c\xb5/3\xef\x19\
+\x9b\x1dTA\x14 \xedw6Bfc\x15L%\x03\
+S@\xa5vtt\xa6\x1f@-\x85\xd6\xd67\xd6\xf8\
+\xf4-\xc0f\xe1;@\xc1T\x09D\x80\x1e}\xc4\xe8\
+L?\x82`\xa3\xc5\xd9\x95\xe6/>r\x9c\xab\xe7n\
+\xect\x1bF\xb4%\x88e\x8b\xfa\x9cx\xc7\xd8\xecG\
+C\xaa]\x15\x0d\xf4\x5c{\x8d\xddV\xb0\x8cb\x00v\
+\x80xx\xf9O][y;p\x22Sdr5\x8e\
+\xddV\xc4\xc5S5i\xfe\xd3\xc5\x0e\xca\x8a/\x90X\
+\x9a@\x92\xb6-uJA\x9f\xf8F%\x00P\xd7V\
+>\x0ftf\x9b1\xbf\x1a\xa7\xc8v\x98\xf3'\x9dF\
+\x12%\x96I\xdc\x8b\x87\x97\x7f\xe9/\xa3\xa8|Uf\
+\xb5\xf0\xf4[\x22sC\x06\xbf\xab\xa2\x81n\xcf+l\
+\x82\xddl\x9f\x18=x\x0d\xc9~\xb5d\x86\xa6T\x0e\
+\xe8\xdb\x03\xf0>=x\xcew\xc1.g\xa2#\x13\xf8\
+\xc1~\x98\xc8\xe5X\x00\xaa\x80\xa6|;$\x03p\x93\
+\xdc|.\x1c\xe8\xc7\xe9\xbe\xdb_E\x87\x0e'\xe81\
+\xfc\xef\x00\x00\x00\x00IEND\xaeB`\x82\
+\x00\x00\x04\xcf\
+\x00\
+\x00\x141x\xda\xc5X\xddS\xe36\x10\x7f\xf7_\xa1\
+\xe6\xe1&\xe9L\x1c>\xae\xd3\x99\xb4\xf4\x06B\xb9\xa3\
+\x93\x9b\x03\x02\xe5\xfa(l%VQ$#\xc9\x04z\
+\xbd\xff\xbd+\xc9vl\xd9\x0e\xa1\xf4\xa8\x9f\xa2\xdd\xf5\
+\xee\xea\xb7\x9f\xceh\x84&\x22}\x94t\x91h\xd4\x9f\
+\x0c\xd0\xde\xce\xee\x8f\xe82!\xe8\x5c\x03g\x99b\xfe\
+\x88\xa6:\x0e\x83\xd1\x08\xcd\xce\x8e?\x0f\xa74\x22\x5c\
+\x91\xe1iL\xb8\xa6sJ\xe4\x18\xe5\xb4\x0b2\x1f\x9e\
+\xeb!\xbc\xb6$2\xa2\x98\xa1O\x17\xe8hv<\xdc\
+\x1fN\x18\xce\x14\x09\x02\xbaL\x85\xd4\xa0\xfc<\xa3\xd1\
+\xadw\x0c'\x82k)\x98Z\xd3\xaf\xc9\xcd\xef\x94\xac\
+|\xc1)~\x14\x99VA\x10\x1c\xa6)\xa3\x11\xd6T\
+\xf0k\xcac\xb1B_\x02\x04\x0f\x8d\xc7he\x09\xf6\
+xO\x15\xbdad\x8c\xb4\xcc\x88\xa5<\x8c\x11\xe5T\
+\x83\x97\x9f\xed\xf9\xb1<\xffa\xcf+\x1a\xeb\xa4\xa4]\
+\x9b\x93\xa5'\xc4`U2>\xd8\xa3\xe5h\xaa\x8d\x85\
+\x95s9\xb4\xc7\xc0r\x96\x84gG\x18\x80\xba\x14\x82\
+\xc1\x8f\xdc\xc7\xc2O\x8e\xef\xe9\xc2^\x01x%\xe7B\
+\xac\xdc5+\xd2\xe6\xc1<J\x84T\xe1\x9c26F\
+)\x96\x10\x87\x9a\x80JqD\xf9b\x8cv\x82\x1a\xdd\
+\x1a\xcf\xb4\x16\xdc\xd3X\xf8q\x83\xa3['\xd0dG\
+\x82\x87Jd2\x82\x1b\xf6\xeed4\x1e12\xd7\xc3\
+\xfd\xbd0\xe5\x8b^C^\xf0\x09\x84\xe5\x96\xc4k<\
+\x16\xe2\x08\xf4\xf7\x07\x0dY\xc21\x84\xa6\x22\x19a\xfe\
+\xde\x0a7D\x1d a*\xc9\x9cHI\xe2k\x17\xa4\
+\x1a\x80a\xb2\x0eI\xf1|}&\x12s!WX\xc6\
+\xdb\x83a\x0b\xe8yh\x9c8\x1b[\x03\x92\xcb\xffo\
+\x98H\xc2\x04\xde\x0a\x92\xc2m#\x0f\x89\x88\xde\xe5 \
+)-\xd2\x02#T\x22G\xe6\x92\xa8\xe49\xd8\xad\xf5\
+\x16\x14\xa3\xb9?@k\x11\xe7l\x0b\xb6\xff\x11Z\xa7\
+\x9a,\xd1\x97.m?4\xc0%\x0f\xfa\x84\x12\x16\xb7\
+`\x9b\xeb0\xe5\x9c\xbf^\xf6(?\x04\x99dVK\
+\x93\xc9\xd3L\x7f$:\x11\xf1\x07\xca\xb5\x1aC\xa7\x0c\
+O\x97\xc9\x95d\x93\x04K\x1ci\x22\xd5'\xce\x1e\xd1\
+\xdf9\xe7\xcc\xba<\x15+\xe8\xd4X5\xcdi\xf0x\
+\x0d'\x18n\x09\xcca\x14\x91TW#\x03r\xe8\x00\
+e\x9a2\xe8OR,\xaf\x14\x91\xa7\xc6\xb7\xbe\xd1\xe7\
+E\xe3\x85\x90>\x95\xaf\xd0o\xdas\xd5]\xedN]\
+\xca~\xef\xbd\xe8\x0d6\xa5\x5cS\xb5y\x00\xc2\x0a\xe2\
+a\x04\x03\x8f\xea\x96dk\x91MhL:$\x9f\x06\
+\xb1\x88\x7f\xd8\x82\xa6\x05\xf4%\xf5\xad\x88\xd6PUj\
+\xfb\xa6W\xbc\xb1M\xed\xb6\x03Yh8\x96\x18\x121\
+\xb43\x17\xee\xdeo\xa5\xff\x82v\x06P\xf3;\xa8\x98\
+\xec9\xfd{\xb4;z\xfb\x04\x18\xafV\xfa\xbb;U\
+\xcb\x95\x9fgR,\xa0\xd1\xa9\xfa\xf8/\xd1Osv\
+\x9d\x93\xcf\xfa\x16\xf4\xcc\xf4-\xa6\x7fh\x0e\xcd4\x17\
+i)p# \xa6\xcb\x86\x88t\xabL.$\x1b7\
+/\x0c}\xc4rAy\xcd\x9c#\xb5k\xf4\xc4+\xb4\
+`S\x88\xf2\xcdj\xbfN\xfd\xcb\xf6\xb2\x94a\x0dC\
+y\x19\x0a\x85\x0e\x0e\x0eP\x0f\xf3X\x0a\x1a\xf7 !\
+\x86\xbb\x90\x11\xc3\xbd\xfakf\x97YH\x91q\xc0\xd6\
+E\xcc\xb3Vn\x84\x9e\xf6\xef\x8cv*T\x0f\xbdy\
+\xd3\xca\x83\xdc\x93\xda\xcbuS\xa6f\xdf\xf2\x02`\xd2\
+\xc1#\xdec\x96y#\xb2\xc8\x0c\xf4\xb3\x11\xafL\xb5\
+\x1a\xb3\xaa\xde]%\xcfKw\xbb\xa0\xad\x94]\xf9\x04\
+\xfe\xe6\xb8)\xec\x13\xc1\xb2%o]<\xa7\xf8\x86\xb0\
+\x96Tt\xfd\xb4\xf7\x1b\xbe\xc7\xb3H\xd2\xd4\x03\xa7\x8e\
+\xfb$!\xb0d\x8a\x87\x8e&\xf4g\xa9\xe4W\xb7\x04\
+\x15\xf2]V\xf3]\xa9\xb5\xf7\x98Wg\x1ak\x023\
+\x90/\xaas\xaa\x00(l\xd83\xdd'*_\x84\x5c\
+39`5\x91x\xb0\xe1^O\x803\x15\x11|\x0e\
+\xc1\x9a\x22\xf1\x82\xbc\x00\x1ff\xf4\xcc\x9c\x9a\xd7A\xa8\
+\xc5\xe27\xc2\xe8\x901\xf8|\x83=\x88 \x0c\x9b\x85\
+R/\xc0\x09\x1b]'\xa0\xea\xd0jz\x1d\xa8<\xa3\
+\xdf4\x95`\x1ak\xa8^\x04\x9f\x069Z\x0e\xb9\xab\
+\x8b\xa9zi~M\x9c\xee\x09\xe6\xee\x22\xe6J\xb0I\
+\xaaWL\xb8.\x17\xfe\x0d\xa6\xb5n\x99\xff\xa1\xe05\
+\xcc\xdc\x89\x92\x06\x8bV\xf9\x85\x7fU\xd9}\xbd\x0e\xea\
+\xad)\xb51\x5c\x88v\x0f\xea\xe2\x9f\x84\x9c\xe7m\x1e\
+\x82O\xddgN\x89\xd4<\xe3\x91YS\xfaf.\x5c\
+\x90\xbb\x8c(=\xf0\xe2H\xe7\xa8\xca\x0ea5\x11r\
+\xa6%\xe8i\xee\x8b\x90CJ0\xe2\x84:_\xfb)\
+hY\x84\xcc_C\x82\x1b\xb7!\xa8\xf0\x9b\x11\xdd\xdc\
+\xf2:\x9byX\x0d!\xda\xd0\x8c\xdfU\xc2\x8b\xec\xa8\
+\xbe\xe2\x91;\xd6,mh\x8b\x1d\xb6\xda\xda\xda\xb6\xd6\
+67\x97\x0e\x83~sx\xd6\xd5\xb6\xac\xc8Mw\xed\
+\xac\xa8m\x1c)J\xe8k\xf0\x0f\x96\x1c\x9e\xf1\
+\x00\x00\x036\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00i\x00\xa1\x006za\
+\x0c\x8d\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x17\x0f(,\xbb\xcb\xc7\xb8\x00\x00\x02\xc3ID\
+ATX\xc3\xcd\x97\xbfOSQ\x14\xc7?\xf7\xb5\x8b\
+\x93&j\x94\x10\xa1\x03\x1a\xa2\xa5\x96\x18\xbbH\xab\xa2\
+\x18\x194\xb0\xc8\xa4U`\xb0\x81\xf4\x0f\x908)\xc6\
+\x99\xc4\xd4 \x12\x8a\x13L\xc4\x1f\x01!\x10\xa0L\x0d\
+\x11JIc\xca\x22\x18\xa2\x83##\xf4:\xf8^\xf3\
+\xca{}?\x10\x027\xe9\xd0{\xce=\xdf\xef;\xe7\
+{\xcf\xbd\x17\x0ey\x087\xce\xb1d\xa8\x02\x08\x03M\
+@\x03\xe0SM?\x80\x05`\x0aH%\xa2\xe9_\xfb\
+F \x96\x0c\x09\xa0\x11\xe8\x03.:\x8c\x9b\x03\xe2\xc0\
+L\x22\x9a\x96{&\x10K\x86\xce\x03c.\x80\xcd\x88\
+\xb4$\xa2\xe9\xb5r\x0e\x8a\x05\xf8\x03 \xff\x1f\xe0\xa8\
+k\xf3j,\xe7\x04b\xc9P\x170\xb2\x8fZ\x1bQ\
+c\xda\x97@e;r@\xa2oKD\xd3\xa3e\x09\
+\xa85\xcf[E\xa8\xad\x08\xe1\xaf\xbcf\x98\xff\xb3\xb5\
+\xc9\xec\xf7Q'$.\xe85!v\xa9}\xd5\xae\xe6\
+\x8a\xf0\xd0\x11yI\xb0\xea\xa6\xc16\xb5\xfa\x81\xb1\xa5\
+7N\x84\xe9\xd7v\x87^\x03\x8dN\x04W\x90;\x0c\
+\xa6\x9e\x93\xd9\x983\xd8\x9a\xfc\x0fi\xa9\xefr\x22\xcc\
+F3\x11\xf69-\xe4Na\x9b\xf7\xa9\x1e2\x1bs\
+\x08!J~w\xea\x1eq\xbf>f\x17\xa2\x88\xe5\xd1\
+u\xb8\xd7n\xd4$e\x81\xe5\x9f\xb3T\x9e\xa8\xe1\xec\
+q_\x89\xad\xe6L\x10Ex\xc9\xff^,\xb7\xfc\xf4\
+\xd5\xd6\xca\xfe\xc5\xb1\xcd--\x03\xe1\xbdHz\xa7\xb0\
+\xcd\xc0\xfc32\x1bsHY\xda\xf0\x9a\x03O\xb8\x17\
+|j\xb5<\x0c\xe0\xd5\xca\xa7\xb7\xd4W\xdfrE\xe4\
+\xdb\xfa4\xd5\xa7.q\xfc\xd8\xc9\x92\xf9\xbbu\x8f\x91\
+\xb2\xc0\xe7L\xbf\xd9\xb2&`T#\xd0\xa0\xb7tF\
+z\x11B \xa5D\x08a\x92~\xe3|9\xdf\xe6@\
+;\x80\x19\x89\x06}\x06|\xe5k\xfd/\xb5\x1a!k\
+]\x98\xfb6\x07\xda\x91H\xbed\xde\xe9\xdd}\x96g\
+\x813!J[RvC\xd1\x9d\xe7\xc6>\xad\xdb^\
+V\xff\xedl\xe3+\x83\xbb\xbf\xbe\x88\xa9\x95`\x01\xa8\
+\xd5,\x03\xf3=\xae\xbe\xc2\xabxi\xbd\x12/\x8aP\
+_\x82\xf1\x95\xc1r\x22\x5c\xd0\x13\x98\x02:5\xcb\xd2\
+\xfa\xb4cp\x8f\xe2\xa53\xf2\xaad\x07h\xe0\x13\xd9\
+\xa1r\xe0\x1af\xb1\x04\xa9\xbd\xd4O\x03\x0f\x9c\x8b\x18\
+l\x13\xd9!>-\xbf\xb5Z\x9e*\x12P\xefp9\
+\xb7\xe0\x1d\xe1^.W]7\xb4\xe3\xaf\xabI;\xf0\
+\x9cvo\xd4\xef\x82\xb8[\xf0`\xf5\x0d\x83m2;\
+\xcc\xc7\xa5\x84]\x88\xb8\xd9a4\xe3$\x0b\x8a\xf0\xd0\
+\x1e~a\x00\x97R2\x99\x1dvz\x1c\xcf\x1c\xbd\x0b\
+\xc9a]\xc9\x0c\x9dPu\xe8>\x00\xf0\xee\xdd\xe0\x96\
+\xef\x82}\xceD\x9b\x19\xf8\xd1~\x98\xa8\xe5X\x03\xfc\
+\xc0m\x97}\x22\xa7\xae\xf1[\x81\x1f\x89\xc7\xe9\xa1\x8f\
+\xbfgH\x11\xb4\x13s\xe2\x92\x00\x00\x00\x00IEN\
+D\xaeB`\x82\
+"
+
+qt_resource_name = b"\
+\x00\x0b\
+\x00\x90;'\
+\x00s\
+\x00t\x00o\x00p\x00-\x003\x002\x00.\x00p\x00n\x00g\
+\x00\x0e\
+\x07\xd4\xda\x07\
+\x00r\
+\x00e\x00f\x00r\x00e\x00s\x00h\x00-\x003\x002\x00.\x00p\x00n\x00g\
+\x00\x0b\
+\x00\x80-\xa7\
+\x00l\
+\x00e\x00f\x00t\x00-\x003\x002\x00.\x00p\x00n\x00g\
+\x00\x08\
+\x08\x01Z\x5c\
+\x00m\
+\x00a\x00i\x00n\x00.\x00q\x00m\x00l\
+\x00\x0c\
+\x0fz\xe9\xa7\
+\x00r\
+\x00i\x00g\x00h\x00t\x00-\x003\x002\x00.\x00p\x00n\x00g\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00>\x00\x00\x00\x00\x00\x01\x00\x00\x07\x9c\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x02\x83\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00Z\x00\x01\x00\x00\x00\x01\x00\x00\x0a\xdf\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00p\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xb2\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
index 1bd1278046073b43bcef5b7874d6f9e1bd9d918d..b7bd74ce40bd6e18d0bddf90a00a5d24ec28cdbc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QDialog, QLineEdit, QVBoxLayout
 
index e423ea347e2d30f3eae5383de2c16470ddd58066..da4219d88960e8d1a7bed63f67397be3a0e6950c 100644 (file)
@@ -1,10 +1,13 @@
 {
     "files": ["../bindings.h",
+              "../bindings.xml",
               "../dialog.py",
               "../macros.h",
               "../main.py",
               "../registerwigglywidget.py",
               "../wigglywidget.cpp",
               "../wigglywidget.h",
-              "../wigglywidget.py"]
+              "../wigglywidget.py",
+              "../CMakeLists.txt",
+              "../../utils/pyside_config.py"]
 }
index 2b24018b1195075f71b113cf4ddb993336931a91..3876b4c637523639bd4b888aa66e12f5b6641362 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index c5560ba6b0c67b4123a2758de1ac68f28ff5e937..f172a94cbca06542d6c3d14a2fac64e1815e1204 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
 from wigglywidget import WigglyWidget
index 3362eab6bf39564fd9b2b693d8296842b06dcc61..08c64a28c6578b5e6d50c6827619565d299271ff 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QBasicTimer, Property
 from PySide6.QtGui import QColor, QFontMetrics, QPainter, QPalette
index 02fc75bf58f712c12a1861f77ec8edc04f1347bf..a014a2f45f7c9573eec312cc9a394cdfb6501062 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2010 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import math
@@ -25,7 +26,7 @@ class Pixmap(QObject):
         super().__init__()
 
         self.pixmap_item = QGraphicsPixmapItem(pix)
-        self.pixmap_item.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+        self.pixmap_item.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
 
     def set_pos(self, pos):
         self.pixmap_item.setPos(pos)
@@ -45,7 +46,7 @@ class Button(QGraphicsWidget):
         self._pix = pixmap
 
         self.setAcceptHoverEvents(True)
-        self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+        self.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
 
     def boundingRect(self):
         return QRectF(-65, -65, 130, 130)
@@ -57,16 +58,16 @@ class Button(QGraphicsWidget):
         return path
 
     def paint(self, painter, option, widget):
-        down = option.state & QStyle.State_Sunken
+        down = option.state & QStyle.StateFlag.State_Sunken
         r = self.boundingRect()
 
         grad = QLinearGradient(r.topLeft(), r.bottomRight())
-        if option.state & QStyle.State_MouseOver:
-            color_0 = Qt.white
+        if option.state & QStyle.StateFlag.State_MouseOver:
+            color_0 = Qt.GlobalColor.white
         else:
-            color_0 = Qt.lightGray
+            color_0 = Qt.GlobalColor.lightGray
 
-        color_1 = Qt.darkGray
+        color_1 = Qt.GlobalColor.darkGray
 
         if down:
             color_0, color_1 = color_1, color_0
@@ -74,12 +75,12 @@ class Button(QGraphicsWidget):
         grad.setColorAt(0, color_0)
         grad.setColorAt(1, color_1)
 
-        painter.setPen(Qt.darkGray)
+        painter.setPen(Qt.GlobalColor.darkGray)
         painter.setBrush(grad)
         painter.drawEllipse(r)
 
-        color_0 = Qt.darkGray
-        color_1 = Qt.lightGray
+        color_0 = Qt.GlobalColor.darkGray
+        color_1 = Qt.GlobalColor.lightGray
 
         if down:
             color_0, color_1 = color_1, color_0
@@ -87,7 +88,7 @@ class Button(QGraphicsWidget):
         grad.setColorAt(0, color_0)
         grad.setColorAt(1, color_1)
 
-        painter.setPen(Qt.NoPen)
+        painter.setPen(Qt.PenStyle.NoPen)
         painter.setBrush(grad)
 
         if down:
@@ -108,7 +109,7 @@ class Button(QGraphicsWidget):
 class View(QGraphicsView):
     def resizeEvent(self, event):
         super(View, self).resizeEvent(event)
-        self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
+        self.fitInView(self.sceneRect(), Qt.AspectRatioMode.KeepAspectRatio)
 
 
 if __name__ == '__main__':
@@ -187,10 +188,11 @@ if __name__ == '__main__':
     # Ui.
     view = View(scene)
     view.setWindowTitle("Animated Tiles")
-    view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+    view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
     view.setBackgroundBrush(QBrush(bg_pix))
-    view.setCacheMode(QGraphicsView.CacheBackground)
-    view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
+    view.setCacheMode(QGraphicsView.CacheModeFlag.CacheBackground)
+    view.setRenderHints(QPainter.RenderHint.Antialiasing
+                        | QPainter.RenderHint.SmoothPixmapTransform)
     view.show()
 
     states = QStateMachine()
@@ -202,7 +204,7 @@ if __name__ == '__main__':
     for i, item in enumerate(items):
         anim = QPropertyAnimation(item, b'pos')
         anim.setDuration(750 + i * 25)
-        anim.setEasingCurve(QEasingCurve.InOutBack)
+        anim.setEasingCurve(QEasingCurve.Type.InOutBack)
         group.addAnimation(anim)
 
     trans = root_state.addTransition(ellipse_button.pressed, ellipse_state)
index 610050210d852300caaf0e13beff4a7201f050af..8386909fe0bf54881d7c528fc5effa7b55e21721 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2010 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -65,7 +66,7 @@ if __name__ == '__main__':
     p4.setGeometry(QRectF(0.0, 236.0, 64.0, 64.0))
 
     scene = QGraphicsScene(0, 0, 300, 300)
-    scene.setBackgroundBrush(Qt.white)
+    scene.setBackgroundBrush(Qt.GlobalColor.white)
     scene.addItem(p1)
     scene.addItem(p2)
     scene.addItem(p3)
@@ -73,12 +74,12 @@ if __name__ == '__main__':
 
     window = QGraphicsView(scene)
     window.setFrameStyle(0)
-    window.setAlignment(Qt.AlignLeft | Qt.AlignTop)
-    window.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
-    window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+    window.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
+    window.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+    window.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
 
     machine = QStateMachine()
-    machine.setGlobalRestorePolicy(QStateMachine.RestoreProperties)
+    machine.setGlobalRestorePolicy(QStateMachine.RestorePolicy.RestoreProperties)
 
     group = QState(machine)
     selected_rect = QRect(86, 86, 128, 128)
index ba50324580264c6c8fcdd9c35efd603571a4f998..5ae9c0be66ddd61217b023cf81a33eb1a6df1a51 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2010 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from enum import IntEnum
 import sys
@@ -64,7 +65,7 @@ class Pixmap(QObject):
         super().__init__()
 
         self.pixmap_item = QGraphicsPixmapItem(pix)
-        self.pixmap_item.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+        self.pixmap_item.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
 
     def set_pos(self, pos):
         self.pixmap_item.setPos(pos)
@@ -109,7 +110,7 @@ class Window(QWidget):
         self._ui.graphicsView.setScene(self._scene)
 
         self._anim = Animation(self._item, b'pos')
-        self._anim.setEasingCurve(QEasingCurve.OutBounce)
+        self._anim.setEasingCurve(QEasingCurve.Type.OutBounce)
         self._ui.easingCurvePicker.setCurrentRow(0)
 
         self.start_animation()
@@ -139,15 +140,15 @@ class Window(QWidget):
 
                 curve_scale = self._iconSize.height() / 2.0
 
-                painter.setPen(Qt.NoPen)
+                painter.setPen(Qt.PenStyle.NoPen)
 
                 # Start point.
-                painter.setBrush(Qt.red)
+                painter.setBrush(Qt.GlobalColor.red)
                 start = QPoint(y_axis, x_axis - curve_scale * curve.valueForProgress(0))
                 painter.drawRect(start.x() - 1, start.y() - 1, 3, 3)
 
                 # End point.
-                painter.setBrush(Qt.blue)
+                painter.setBrush(Qt.GlobalColor.blue)
                 end = QPoint(y_axis + curve_scale,
                              x_axis - curve_scale * curve.valueForProgress(1))
                 painter.drawRect(end.x() - 1, end.y() - 1, 3, 3)
@@ -161,9 +162,9 @@ class Window(QWidget):
                     curve_path.lineTo(to)
                     t += 1.0 / curve_scale
 
-                painter.setRenderHint(QPainter.Antialiasing, True)
+                painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
                 painter.strokePath(curve_path, QColor(32, 32, 32))
-                painter.setRenderHint(QPainter.Antialiasing, False)
+                painter.setRenderHint(QPainter.RenderHint.Antialiasing, False)
 
                 item = QListWidgetItem()
                 item.setIcon(QIcon(pix))
@@ -182,15 +183,16 @@ class Window(QWidget):
         self._anim.setEasingCurve(curve_type)
         self._anim.setCurrentTime(0)
 
-        is_elastic = (curve_type.value >= QEasingCurve.InElastic.value
-                      and curve_type.value <= QEasingCurve.OutInElastic.value)
-        is_bounce = (curve_type.value >= QEasingCurve.InBounce.value
-                     and curve_type.value <= QEasingCurve.OutInBounce.value)
+        is_elastic = (curve_type.value >= QEasingCurve.Type.InElastic.value
+                      and curve_type.value <= QEasingCurve.Type.OutInElastic.value)
+        is_bounce = (curve_type.value >= QEasingCurve.Type.InBounce.value
+                     and curve_type.value <= QEasingCurve.Type.OutInBounce.value)
 
         self._ui.periodSpinBox.setEnabled(is_elastic)
         self._ui.amplitudeSpinBox.setEnabled(is_elastic or is_bounce)
-        self._ui.overshootSpinBox.setEnabled(curve_type.value >= QEasingCurve.InBack.value
-                                             and curve_type.value <= QEasingCurve.OutInBack.value)
+        overshoot = (curve_type.value >= QEasingCurve.Type.InBack.value
+                     and curve_type.value <= QEasingCurve.Type.OutInBack.value)
+        self._ui.overshootSpinBox.setEnabled(overshoot)
 
     def path_changed(self, index):
         self._anim.set_path_type(index)
index 364aebeda642b96bfeb9c75a3e57d62eb3d5efa0..1a6340a9d40ba747967074c6762024a89b34c4bb 100644 (file)
       </size>
      </property>
      <property name="verticalScrollBarPolicy">
-      <enum>Qt::ScrollBarAlwaysOff</enum>
+      <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
      </property>
      <property name="movement">
-      <enum>QListView::Static</enum>
+      <enum>QListView::Movement::Static</enum>
      </property>
      <property name="isWrapping" stdset="0">
       <bool>false</bool>
      </property>
      <property name="viewMode">
-      <enum>QListView::IconMode</enum>
+      <enum>QListView::ViewMode::IconMode</enum>
      </property>
      <property name="selectionRectVisible">
       <bool>false</bool>
@@ -68,7 +68,7 @@
            </size>
           </property>
           <property name="layoutDirection">
-           <enum>Qt::LeftToRight</enum>
+           <enum>Qt::LayoutDirection::LeftToRight</enum>
           </property>
           <property name="text">
            <string>Line</string>
@@ -77,7 +77,7 @@
            <bool>true</bool>
           </property>
           <attribute name="buttonGroup">
-           <string>buttonGroup</string>
+           <string notr="true">buttonGroup</string>
           </attribute>
          </widget>
         </item>
@@ -93,7 +93,7 @@
            <string>Circle</string>
           </property>
           <attribute name="buttonGroup">
-           <string>buttonGroup</string>
+           <string notr="true">buttonGroup</string>
           </attribute>
          </widget>
         </item>
        </property>
        <layout class="QFormLayout" name="formLayout">
         <property name="fieldGrowthPolicy">
-         <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+         <enum>QFormLayout::FieldGrowthPolicy::AllNonFixedFieldsGrow</enum>
         </property>
         <item row="0" column="0">
          <widget class="QLabel" name="label">
      <item>
       <spacer name="verticalSpacer">
        <property name="orientation">
-        <enum>Qt::Vertical</enum>
+        <enum>Qt::Orientation::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
index 2925cbae70d4cdbf92b16b2fac55128a5c59118e..c879bb59d06f49c9ba0666a624181fa153534d82 100644 (file)
@@ -3,7 +3,7 @@
 ################################################################################
 ## Form generated from reading UI file 'form.ui'
 ##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.1
 ##
 ## WARNING! All changes made in this file will be lost when recompiling UI file!
 ################################################################################
@@ -35,10 +35,10 @@ class Ui_Form(object):
         sizePolicy.setHeightForWidth(self.easingCurvePicker.sizePolicy().hasHeightForWidth())
         self.easingCurvePicker.setSizePolicy(sizePolicy)
         self.easingCurvePicker.setMaximumSize(QSize(16777215, 120))
-        self.easingCurvePicker.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
-        self.easingCurvePicker.setMovement(QListView.Static)
-        self.easingCurvePicker.setProperty("isWrapping", False)
-        self.easingCurvePicker.setViewMode(QListView.IconMode)
+        self.easingCurvePicker.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+        self.easingCurvePicker.setMovement(QListView.Movement.Static)
+        self.easingCurvePicker.setProperty(u"isWrapping", False)
+        self.easingCurvePicker.setViewMode(QListView.ViewMode.IconMode)
         self.easingCurvePicker.setSelectionRectVisible(False)
 
         self.gridLayout.addWidget(self.easingCurvePicker, 0, 0, 1, 2)
@@ -56,7 +56,7 @@ class Ui_Form(object):
         self.buttonGroup.addButton(self.lineRadio)
         self.lineRadio.setObjectName(u"lineRadio")
         self.lineRadio.setMaximumSize(QSize(16777215, 40))
-        self.lineRadio.setLayoutDirection(Qt.LeftToRight)
+        self.lineRadio.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
         self.lineRadio.setChecked(True)
 
         self.gridLayout_2.addWidget(self.lineRadio, 0, 0, 1, 1)
@@ -80,7 +80,7 @@ class Ui_Form(object):
         self.groupBox.setSizePolicy(sizePolicy1)
         self.formLayout = QFormLayout(self.groupBox)
         self.formLayout.setObjectName(u"formLayout")
-        self.formLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+        self.formLayout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
         self.label = QLabel(self.groupBox)
         self.label.setObjectName(u"label")
         sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
@@ -90,7 +90,7 @@ class Ui_Form(object):
         self.label.setSizePolicy(sizePolicy2)
         self.label.setMinimumSize(QSize(0, 30))
 
-        self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label)
+        self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label)
 
         self.periodSpinBox = QDoubleSpinBox(self.groupBox)
         self.periodSpinBox.setObjectName(u"periodSpinBox")
@@ -105,7 +105,7 @@ class Ui_Form(object):
         self.periodSpinBox.setSingleStep(0.100000000000000)
         self.periodSpinBox.setValue(-1.000000000000000)
 
-        self.formLayout.setWidget(0, QFormLayout.FieldRole, self.periodSpinBox)
+        self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.periodSpinBox)
 
         self.amplitudeSpinBox = QDoubleSpinBox(self.groupBox)
         self.amplitudeSpinBox.setObjectName(u"amplitudeSpinBox")
@@ -115,13 +115,13 @@ class Ui_Form(object):
         self.amplitudeSpinBox.setSingleStep(0.100000000000000)
         self.amplitudeSpinBox.setValue(-1.000000000000000)
 
-        self.formLayout.setWidget(2, QFormLayout.FieldRole, self.amplitudeSpinBox)
+        self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.amplitudeSpinBox)
 
         self.label_3 = QLabel(self.groupBox)
         self.label_3.setObjectName(u"label_3")
         self.label_3.setMinimumSize(QSize(0, 30))
 
-        self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_3)
+        self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label_3)
 
         self.overshootSpinBox = QDoubleSpinBox(self.groupBox)
         self.overshootSpinBox.setObjectName(u"overshootSpinBox")
@@ -131,13 +131,13 @@ class Ui_Form(object):
         self.overshootSpinBox.setSingleStep(0.100000000000000)
         self.overshootSpinBox.setValue(-1.000000000000000)
 
-        self.formLayout.setWidget(4, QFormLayout.FieldRole, self.overshootSpinBox)
+        self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.overshootSpinBox)
 
         self.label_2 = QLabel(self.groupBox)
         self.label_2.setObjectName(u"label_2")
         self.label_2.setMinimumSize(QSize(0, 30))
 
-        self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_2)
+        self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_2)
 
 
         self.verticalLayout.addWidget(self.groupBox)
index 509c3a7aa4f3f94c26501c008138d0a4d75a1446..730aab1b3c22999f8a6d8970069bc188f4d684d0 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2010 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -64,7 +65,7 @@ if __name__ == '__main__':
 
     # Parent widget.
     widget = QGraphicsWidget()
-    layout = QGraphicsLinearLayout(Qt.Vertical, widget)
+    layout = QGraphicsLinearLayout(Qt.Orientation.Vertical, widget)
     layout.addItem(edit_proxy)
     layout.addItem(button_proxy)
     widget.setLayout(layout)
index 8b87b5e5e0213f719bc6f43261e3d8a09019631b..ea7cfa0b0ece02df2f45cc6f6b74d043db5d0e8e 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # Resource object code
 #
index a5688da7a3b77a1f330c43aa313df683b4d71d69..fba1f71b07ab4d7aac079155ea0ec6f20715cef1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/desktop/screenshot example from Qt v6.x"""
 
@@ -20,8 +21,9 @@ class Screenshot(QWidget):
 
         self.screenshot_label = QLabel(self)
 
-        self.screenshot_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
-        self.screenshot_label.setAlignment(Qt.AlignCenter)
+        self.screenshot_label.setSizePolicy(QSizePolicy.Policy.Expanding,
+                                            QSizePolicy.Policy.Expanding)
+        self.screenshot_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
 
         screen_geometry: QRect = self.screen().geometry()
         self.screenshot_label.setMinimumSize(
@@ -55,7 +57,7 @@ class Screenshot(QWidget):
         save_screenshot_button.clicked.connect(self.save_screenshot)
         buttons_layout.addWidget(save_screenshot_button)
         quit_screenshot_button = QPushButton("Quit", self)
-        quit_screenshot_button.setShortcut(Qt.CTRL | Qt.Key_Q)
+        quit_screenshot_button.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Q)
         quit_screenshot_button.clicked.connect(self.close)
         buttons_layout.addWidget(quit_screenshot_button)
         buttons_layout.addStretch()
@@ -69,7 +71,7 @@ class Screenshot(QWidget):
 
     def resizeEvent(self, event):
         scaled_size = self.original_pixmap.size()
-        scaled_size.scale(self.screenshot_label.size(), Qt.KeepAspectRatio)
+        scaled_size.scale(self.screenshot_label.size(), Qt.AspectRatioMode.KeepAspectRatio)
         if scaled_size != self.screenshot_label.pixmap().size():
             self.update_screenshot_label()
 
@@ -84,14 +86,14 @@ class Screenshot(QWidget):
     @Slot()
     def save_screenshot(self):
         fmt = "png"  # In order to avoid shadowing built-in format
-        initial_path = QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
+        initial_path = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.PicturesLocation)  # noqa: E501
         if not initial_path:
             initial_path = QDir.currentPath()
         initial_path += f"/untitled.{fmt}"
 
         fileDialog = QFileDialog(self, "Save As", initial_path)
-        fileDialog.setAcceptMode(QFileDialog.AcceptSave)
-        fileDialog.setFileMode(QFileDialog.AnyFile)
+        fileDialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
+        fileDialog.setFileMode(QFileDialog.FileMode.AnyFile)
         fileDialog.setDirectory(initial_path)
         mime_types = []
 
@@ -100,7 +102,7 @@ class Screenshot(QWidget):
         fileDialog.setMimeTypeFilters(mime_types)
         fileDialog.selectMimeTypeFilter("image/" + fmt)
         fileDialog.setDefaultSuffix(fmt)
-        if fileDialog.exec() != QDialog.Accepted:
+        if fileDialog.exec() != QDialog.DialogCode.Accepted:
             return
 
         file_name = fileDialog.selectedFiles()[0]
@@ -142,8 +144,8 @@ class Screenshot(QWidget):
         self.screenshot_label.setPixmap(
             self.original_pixmap.scaled(
                 self.screenshot_label.size(),
-                Qt.KeepAspectRatio,
-                Qt.SmoothTransformation,
+                Qt.AspectRatioMode.KeepAspectRatio,
+                Qt.TransformationMode.SmoothTransformation,
             )
         )
 
diff --git a/examples/widgets/desktop/systray/doc/systray.png b/examples/widgets/desktop/systray/doc/systray.png
new file mode 100644 (file)
index 0000000..5537c09
Binary files /dev/null and b/examples/widgets/desktop/systray/doc/systray.png differ
diff --git a/examples/widgets/desktop/systray/doc/systray.rst b/examples/widgets/desktop/systray/doc/systray.rst
new file mode 100644 (file)
index 0000000..364e7c9
--- /dev/null
@@ -0,0 +1,10 @@
+System Tray Icon Example
+========================
+
+The System Tray Icon example shows how to add an icon with a menu and popup
+messages to a desktop environment's system tray. It demonstrates the analogous
+Qt example `System Tray Icon Example
+<https://doc.qt.io/qt-6/qtwidgets-desktop-systray-example.html>`_.
+
+.. image:: systray.png
+   :align: center
index c52c51387495b33040cd4029c190b0511bcac95c..38e7fbb20071381d5d15d594efb684caf87a9335 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 0b5bc39dc98f638634c7fa4519aafabea6f34cb3..ec725239e831bf2aa967ee99959957f7b50b51df 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtGui import QAction, QIcon
@@ -156,21 +157,21 @@ class Window(QDialog):
         self._type_label = QLabel("Type:")
 
         self._type_combo_box = QComboBox()
-        self._type_combo_box.addItem("None", QSystemTrayIcon.NoIcon)
+        self._type_combo_box.addItem("None", QSystemTrayIcon.MessageIcon.NoIcon)
         self._type_combo_box.addItem(
-            self.style().standardIcon(QStyle.SP_MessageBoxInformation),
+            self.style().standardIcon(QStyle.StandardPixmap.SP_MessageBoxInformation),
             "Information",
-            QSystemTrayIcon.Information,
+            QSystemTrayIcon.MessageIcon.Information,
         )
         self._type_combo_box.addItem(
-            self.style().standardIcon(QStyle.SP_MessageBoxWarning),
+            self.style().standardIcon(QStyle.StandardPixmap.SP_MessageBoxWarning),
             "Warning",
-            QSystemTrayIcon.Warning,
+            QSystemTrayIcon.MessageIcon.Warning,
         )
         self._type_combo_box.addItem(
-            self.style().standardIcon(QStyle.SP_MessageBoxCritical),
+            self.style().standardIcon(QStyle.StandardPixmap.SP_MessageBoxCritical),
             "Critical",
-            QSystemTrayIcon.Critical,
+            QSystemTrayIcon.MessageIcon.Critical,
         )
         self._type_combo_box.addItem(QIcon(), "Custom icon", -1)
         self._type_combo_box.setCurrentIndex(1)
index 0efbde69f6cd7bf52aea553d988f02dc02033dfa..d7014313f75074f10764ba5553b2d763b3961978 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import os
 from pathlib import Path
@@ -64,9 +65,9 @@ class ClassWizard(QWizard):
         self._output_index = self.addPage(OutputFilesPage())
         self.addPage(ConclusionPage())
 
-        self.setPixmap(QWizard.BannerPixmap,
+        self.setPixmap(QWizard.WizardPixmap.BannerPixmap,
                        QPixmap(':/images/banner.png'))
-        self.setPixmap(QWizard.BackgroundPixmap,
+        self.setPixmap(QWizard.WizardPixmap.BackgroundPixmap,
                        QPixmap(':/images/background.png'))
 
         self.setWindowTitle("Class Wizard")
@@ -217,7 +218,7 @@ class IntroPage(QWizardPage):
         super().__init__(parent)
 
         self.setTitle("Introduction")
-        self.setPixmap(QWizard.WatermarkPixmap,
+        self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
                        QPixmap(':/images/watermark1.png'))
 
         label = QLabel(INTRODUCTION)
@@ -234,7 +235,7 @@ class ClassInfoPage(QWizardPage):
         self.setTitle("Class Information")
         self.setSubTitle("Specify basic information about the class for "
                          "which you want to generate a skeleton source code file.")
-        self.setPixmap(QWizard.LogoPixmap,
+        self.setPixmap(QWizard.WizardPixmap.LogoPixmap,
                        QPixmap(':/qt-project.org/logos/pysidelogo.png'))
 
         class_name_line_edit = QLineEdit()
@@ -277,7 +278,7 @@ class QObjectPage(QWizardPage):
 
         self.setTitle("QObject parameters")
         self.setSubTitle("Specify the signals, slots and properties.")
-        self.setPixmap(QWizard.LogoPixmap,
+        self.setPixmap(QWizard.WizardPixmap.LogoPixmap,
                        QPixmap(':/qt-project.org/logos/pysidelogo.png'))
         layout = QVBoxLayout(self)
         self._properties_chooser = PropertyChooser()
@@ -295,7 +296,7 @@ class OutputFilesPage(QWizardPage):
         self.setTitle("Output Files")
         self.setSubTitle("Specify where you want the wizard to put the "
                          "generated skeleton code.")
-        self.setPixmap(QWizard.LogoPixmap,
+        self.setPixmap(QWizard.WizardPixmap.LogoPixmap,
                        QPixmap(':/qt-project.org/logos/pysidelogo.png'))
 
         output_dir_label = QLabel("&Output directory:")
@@ -353,7 +354,7 @@ class ConclusionPage(QWizardPage):
         super().__init__(parent)
 
         self.setTitle("Conclusion")
-        self.setPixmap(QWizard.WatermarkPixmap,
+        self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
                        QPixmap(':/images/watermark1.png'))
 
         self.label = QLabel()
@@ -367,7 +368,7 @@ class ConclusionPage(QWizardPage):
         layout.addWidget(self._launch_check_box)
 
     def initializePage(self):
-        finish_text = self.wizard().buttonText(QWizard.FinishButton)
+        finish_text = self.wizard().buttonText(QWizard.WizardButton.FinishButton)
         finish_text = finish_text.replace('&', '')
         self.label.setText(f"Click {finish_text} to generate the class skeleton.")
         self._launch_check_box.setChecked(True)
index 6bf47c6d17e69798bdad76b690142c73bbc1a3b4..f7239ae3cab9ffd8b4a3ef11718f209a318b57f0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QRegularExpression, Property, Slot
 from PySide6.QtGui import QRegularExpressionValidator
@@ -33,7 +34,8 @@ class ValidatingInputDialog(QDialog):
         self._form_layout.addRow(label, self._lineedit)
         layout.addLayout(self._form_layout)
 
-        bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+                              | QDialogButtonBox.StandardButton.Cancel)
         layout.addWidget(bb)
         bb.rejected.connect(self.reject)
         bb.accepted.connect(self.accept)
diff --git a/examples/widgets/dialogs/extension/doc/extension.png b/examples/widgets/dialogs/extension/doc/extension.png
new file mode 100644 (file)
index 0000000..407af27
Binary files /dev/null and b/examples/widgets/dialogs/extension/doc/extension.png differ
diff --git a/examples/widgets/dialogs/extension/doc/extension.rst b/examples/widgets/dialogs/extension/doc/extension.rst
new file mode 100644 (file)
index 0000000..d64db23
--- /dev/null
@@ -0,0 +1,11 @@
+Extension Example
+=================
+
+This example demonstrates a PySide6 application that creates a find dialog with
+an extendable interface, showcasing how to add and manage additional options
+dynamically within a dialog. It demonstrates the analogous Qt example
+`Extension Example <https://doc.qt.io/qt-6.2/qtwidgets-dialogs-extension-example.html>`_.
+
+.. image:: extension.png
+    :width: 400
+    :alt: extension screenshot
index b8919f38e527dd9c2db19428d4448a2f6d0f9d16..bed9a1a32ad6b4ffde1794604374a59c22ae54ce 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/dialogs/extension example from Qt v5.x"""
 
@@ -32,9 +33,9 @@ class FindDialog(QDialog):
         more_button.setCheckable(True)
         more_button.setAutoDefault(False)
 
-        button_box = QDialogButtonBox(Qt.Vertical)
-        button_box.addButton(find_button, QDialogButtonBox.ActionRole)
-        button_box.addButton(more_button, QDialogButtonBox.ActionRole)
+        button_box = QDialogButtonBox(Qt.Orientation.Vertical)
+        button_box.addButton(find_button, QDialogButtonBox.ButtonRole.ActionRole)
+        button_box.addButton(more_button, QDialogButtonBox.ButtonRole.ActionRole)
 
         extension = QWidget()
 
@@ -62,7 +63,7 @@ class FindDialog(QDialog):
         left_layout.addStretch(1)
 
         main_layout = QGridLayout(self)
-        main_layout.setSizeConstraint(QLayout.SetFixedSize)
+        main_layout.setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
         main_layout.addLayout(left_layout, 0, 0)
         main_layout.addWidget(button_box, 0, 1)
         main_layout.addWidget(extension, 1, 0, 1, 2)
index 28667d17309c77411fae7d39454ff82d0a35d0ec..0fdeffbdb92f33e4ad20295cde7c6054901d5dd0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from enum import IntEnum
@@ -31,7 +32,8 @@ class IntroPage(QWizardPage):
 
         path = Path(__file__).resolve().parent
 
-        self.setPixmap(QWizard.WatermarkPixmap, QPixmap(path / "images" / "watermark.png"))
+        self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
+                       QPixmap(path / "images" / "watermark.png"))
         self.top_label = QLabel(
             "This wizard will help you register your copy of "
             "<i>Super Product One</i>&trade; or start "
@@ -165,7 +167,8 @@ class ConclusionPage(QWizardPage):
         self.setTitle("Complete Your Registration")
 
         path = Path(__file__).resolve().parent
-        self.setPixmap(QWizard.WatermarkPixmap, QPixmap(path / "images" / "watermark.png"))
+        self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
+                       QPixmap(path / "images" / "watermark.png"))
 
         self.bottom_label = QLabel()
         self.bottom_label.setWordWrap(True)
@@ -206,14 +209,14 @@ class ConclusionPage(QWizardPage):
     def setVisible(self, visible: bool):
         super().setVisible(visible)
         if visible:
-            self.wizard().setButtonText(QWizard.CustomButton1, "&Print")
-            self.wizard().setOption(QWizard.HaveCustomButton1, True)
+            self.wizard().setButtonText(QWizard.WizardButton.CustomButton1, "&Print")
+            self.wizard().setOption(QWizard.WizardOption.HaveCustomButton1, True)
 
             if not self.custom_button_clicked_signal_connected:
                 self.custom_button_clicked_signal_connected = True
                 self.wizard().customButtonClicked.connect(self.print_button_clicked)
         else:
-            self.wizard().setOption(QWizard.HaveCustomButton1, False)
+            self.wizard().setOption(QWizard.WizardOption.HaveCustomButton1, False)
 
             if self.custom_button_clicked_signal_connected:
                 self.custom_button_clicked_signal_connected = False
@@ -250,12 +253,12 @@ class LicenseWizard(QWizard):
         self.setStartId(Pages.Page_Intro)
 
         if sys.platform == 'darwin':
-            self.setWizardStyle(QWizard.ModernStyle)
+            self.setWizardStyle(QWizard.WizardStyle.ModernStyle)
 
-        self.setOption(QWizard.HaveHelpButton, True)
+        self.setOption(QWizard.WizardOption.HaveHelpButton, True)
 
         path = Path(__file__).resolve().parent
-        self.setPixmap(QWizard.LogoPixmap, QPixmap(path / "images" / "logo.png"))
+        self.setPixmap(QWizard.WizardPixmap.LogoPixmap, QPixmap(path / "images" / "logo.png"))
 
         self.helpRequested.connect(self.show_help)
         self.setWindowTitle("License Wizard")
index 26537ddf90a45df29cf87cca53c58d1a21e7a012..4e21673426dca3d04402c230a3582d8d4aadb483 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
diff --git a/examples/widgets/dialogs/standarddialogs/doc/standarddialogs.png b/examples/widgets/dialogs/standarddialogs/doc/standarddialogs.png
new file mode 100644 (file)
index 0000000..6598710
Binary files /dev/null and b/examples/widgets/dialogs/standarddialogs/doc/standarddialogs.png differ
diff --git a/examples/widgets/dialogs/standarddialogs/doc/standarddialogs.rst b/examples/widgets/dialogs/standarddialogs/doc/standarddialogs.rst
new file mode 100644 (file)
index 0000000..83bc6ca
--- /dev/null
@@ -0,0 +1,8 @@
+Standard Dialogs Example
+========================
+
+The Standard Dialogs example shows the standard dialogs that are provided by
+Qt.
+
+.. image:: standarddialogs.png
+   :align: center
index ef677d5a810197fb343ec5403439fe918dfc6967..7bd68620a4226456a0e869b62e94e6291019ee52 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/dialogs/standarddialogs example from Qt v5.x"""
 
@@ -20,13 +21,14 @@ from PySide6.QtWidgets import (QApplication, QColorDialog, QCheckBox, QDialog,
 class DialogOptionsWidget(QGroupBox):
     """Widget displaying a number of check boxes representing the dialog
        options."""
-    def __init__(self, parent=None):
+    def __init__(self, zero_value, parent=None):
         super().__init__(parent)
+        self._zero_value = zero_value
         self._layout = QVBoxLayout(self)
         self._mapping = {}
 
     def value(self):
-        result = 0
+        result = self._zero_value
         for checkbox, value in self._mapping.items():
             if checkbox.isChecked():
                 result |= value
@@ -47,7 +49,7 @@ class Dialog(QDialog):
 
         self._error_message_dialog = QErrorMessage(self)
 
-        frame_style = QFrame.Sunken | QFrame.Panel
+        frame_style = QFrame.Shadow.Sunken | QFrame.Shape.Panel
 
         self._integer_label = QLabel()
         self._integer_label.setFrameStyle(frame_style)
@@ -72,27 +74,27 @@ class Dialog(QDialog):
         self._color_label = QLabel()
         self._color_label.setFrameStyle(frame_style)
         self._color_button = QPushButton("QColorDialog.get&Color()")
-        self._color_options = DialogOptionsWidget()
+        self._color_options = DialogOptionsWidget(QColorDialog.ColorDialogOption(0))
         self._color_options.add_checkbox("Show alpha channel",
-                                         QColorDialog.ShowAlphaChannel)
+                                         QColorDialog.ColorDialogOption.ShowAlphaChannel)
         self._color_options.add_checkbox("No buttons",
-                                         QColorDialog.NoButtons)
+                                         QColorDialog.ColorDialogOption.NoButtons)
 
         self._font_label = QLabel()
         self._font_label.setFrameStyle(frame_style)
         self._font_button = QPushButton("QFontDialog.get&Font()")
-        self._font_options = DialogOptionsWidget()
+        self._font_options = DialogOptionsWidget(QFontDialog.FontDialogOption(0))
         self._font_options.add_checkbox("Do not use native dialog",
-                                        QFontDialog.DontUseNativeDialog)
+                                        QFontDialog.FontDialogOption.DontUseNativeDialog)
         self._font_options.add_checkbox("Show scalable fonts",
-                                        QFontDialog.ScalableFonts)
+                                        QFontDialog.FontDialogOption.ScalableFonts)
         self._font_options.add_checkbox("Show non-scalable fonts",
-                                        QFontDialog.NonScalableFonts)
+                                        QFontDialog.FontDialogOption.NonScalableFonts)
         self._font_options.add_checkbox("Show monospaced fonts",
-                                        QFontDialog.MonospacedFonts)
+                                        QFontDialog.FontDialogOption.MonospacedFonts)
         self._font_options.add_checkbox("Show proportional fonts",
-                                        QFontDialog.ProportionalFonts)
-        self._font_options.add_checkbox("No buttons", QFontDialog.NoButtons)
+                                        QFontDialog.FontDialogOption.ProportionalFonts)
+        self._font_options.add_checkbox("No buttons", QFontDialog.FontDialogOption.NoButtons)
 
         self._directory_label = QLabel()
         self._directory_label.setFrameStyle(frame_style)
@@ -110,20 +112,20 @@ class Dialog(QDialog):
         self._save_file_name_label.setFrameStyle(frame_style)
         self._save_file_name_button = QPushButton("QFileDialog.get&SaveFileName()")
 
-        self._file_options = DialogOptionsWidget()
+        self._file_options = DialogOptionsWidget(QFileDialog.Option(0))
         self._file_options.add_checkbox("Do not use native dialog",
-                                        QFileDialog.DontUseNativeDialog)
+                                        QFileDialog.Option.DontUseNativeDialog)
         self._file_options.add_checkbox("Show directories only",
-                                        QFileDialog.ShowDirsOnly)
+                                        QFileDialog.Option.ShowDirsOnly)
         self._file_options.add_checkbox("Do not resolve symlinks",
-                                        QFileDialog.DontResolveSymlinks)
+                                        QFileDialog.Option.DontResolveSymlinks)
         self._file_options.add_checkbox("Do not confirm overwrite",
-                                        QFileDialog.DontConfirmOverwrite)
-        self._file_options.add_checkbox("Readonly", QFileDialog.ReadOnly)
+                                        QFileDialog.Option.DontConfirmOverwrite)
+        self._file_options.add_checkbox("Readonly", QFileDialog.Option.ReadOnly)
         self._file_options.add_checkbox("Hide name filter details",
-                                        QFileDialog.HideNameFilterDetails)
+                                        QFileDialog.Option.HideNameFilterDetails)
         self._file_options.add_checkbox("Do not use custom directory icons (Windows)",
-                                        QFileDialog.DontUseCustomDirectoryIcons)
+                                        QFileDialog.Option.DontUseCustomDirectoryIcons)
 
         self._critical_label = QLabel()
         self._critical_label.setFrameStyle(frame_style)
@@ -177,7 +179,7 @@ class Dialog(QDialog):
         layout.addWidget(self._text_label, 3, 1)
         layout.addWidget(self._multiline_text_label, 4, 1)
         layout.addWidget(self._multiline_text_button, 4, 0)
-        spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+        spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
         layout.addItem(spacer, 5, 0)
         toolbox.addItem(page, "Input Dialogs")
 
@@ -185,7 +187,7 @@ class Dialog(QDialog):
         layout = QGridLayout(page)
         layout.addWidget(self._color_button, 0, 0)
         layout.addWidget(self._color_label, 0, 1)
-        spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+        spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
         layout.addItem(spacer, 1, 0)
         layout.addWidget(self._color_options, 2, 0, 1, 2)
         toolbox.addItem(page, "Color Dialog")
@@ -194,7 +196,7 @@ class Dialog(QDialog):
         layout = QGridLayout(page)
         layout.addWidget(self._font_button, 0, 0)
         layout.addWidget(self._font_label, 0, 1)
-        spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+        spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
         layout.addItem(spacer, 1, 0)
         layout.addWidget(self._font_options, 2, 0, 1, 2)
         toolbox.addItem(page, "Font Dialog")
@@ -209,7 +211,7 @@ class Dialog(QDialog):
         layout.addWidget(self._open_file_names_label, 2, 1)
         layout.addWidget(self._save_file_name_button, 3, 0)
         layout.addWidget(self._save_file_name_label, 3, 1)
-        spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+        spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
         layout.addItem(spacer, 4, 0)
         layout.addWidget(self._file_options, 5, 0, 1, 2)
 
@@ -226,7 +228,7 @@ class Dialog(QDialog):
         layout.addWidget(self._warning_button, 3, 0)
         layout.addWidget(self._warning_label, 3, 1)
         layout.addWidget(self._error_button, 4, 0)
-        spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+        spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
         layout.addItem(spacer, 5, 0)
         toolbox.addItem(page, "Message Boxes")
 
@@ -271,8 +273,8 @@ class Dialog(QDialog):
     @Slot()
     def set_color(self):
         options_value = self._color_options.value()
-        options = QColorDialog.ColorDialogOptions(options_value)
-        color = QColorDialog.getColor(Qt.green, self, "Select Color", options)
+        options = QColorDialog.ColorDialogOption(options_value)
+        color = QColorDialog.getColor(Qt.GlobalColor.green, self, "Select Color", options)
 
         if color.isValid():
             self._color_label.setText(color.name())
@@ -345,15 +347,15 @@ class Dialog(QDialog):
                    Activating the liquid oxygen stirring fans caused an
                    explosion in one of the tanks. Liquid oxygen levels
                    are getting low. This may jeopardize the moon landing mission.""")
-        msg_box = QMessageBox(QMessageBox.Critical, "QMessageBox.critical()",
+        msg_box = QMessageBox(QMessageBox.Icon.Critical, "QMessageBox.critical()",
                               "Houston, we have a problem",
-                              QMessageBox.Abort | QMessageBox.Retry | QMessageBox.Ignore,
-                              self)
+                              QMessageBox.StandardButton.Abort | QMessageBox.StandardButton.Retry
+                              | QMessageBox.StandardButton.Ignore, self)
         msg_box.setInformativeText(m)
         reply = msg_box.exec()
-        if reply == QMessageBox.Abort:
+        if reply == QMessageBox.StandardButton.Abort:
             self._critical_label.setText("Abort")
-        elif reply == QMessageBox.Retry:
+        elif reply == QMessageBox.StandardButton.Retry:
             self._critical_label.setText("Retry")
         else:
             self._critical_label.setText("Ignore")
@@ -365,12 +367,12 @@ class Dialog(QDialog):
                    the conclusion of Elvis Presley concerts in order to
                    disperse audiences who lingered in hopes of an encore.
                    It has since become a catchphrase and punchline.""")
-        msg_box = QMessageBox(QMessageBox.Information, "QMessageBox.information()",
+        msg_box = QMessageBox(QMessageBox.Icon.Information, "QMessageBox.information()",
                               "Elvis has left the building.",
-                              QMessageBox.Ok, self)
+                              QMessageBox.StandardButton.Ok, self)
         msg_box.setInformativeText(m)
         reply = msg_box.exec()
-        if reply == QMessageBox.Ok:
+        if reply == QMessageBox.StandardButton.Ok:
             self._information_label.setText("OK")
         else:
             self._information_label.setText("Escape")
@@ -383,29 +385,30 @@ class Dialog(QDialog):
                    meat patty. The cheese is usually added to the cooking
                    hamburger patty shortly before serving, which allows the
                    cheese to melt.""")
-        msg_box = QMessageBox(QMessageBox.Question, "QMessageBox.question()",
+        msg_box = QMessageBox(QMessageBox.Icon.Question, "QMessageBox.question()",
                               "Would you like cheese with that?",
-                              QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
+                              QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
+                              | QMessageBox.StandardButton.Cancel)
         msg_box.setInformativeText(m)
         reply = msg_box.exec()
-        if reply == QMessageBox.Yes:
+        if reply == QMessageBox.StandardButton.Yes:
             self._question_label.setText("Yes")
-        elif reply == QMessageBox.No:
+        elif reply == QMessageBox.StandardButton.No:
             self._question_label.setText("No")
         else:
             self._question_label.setText("Cancel")
 
     @Slot()
     def warning_message(self):
-        msg_box = QMessageBox(QMessageBox.Warning, "QMessageBox.warning()",
+        msg_box = QMessageBox(QMessageBox.Icon.Warning, "QMessageBox.warning()",
                               "Delete the only copy of your movie manuscript?",
-                              QMessageBox.NoButton, self)
+                              QMessageBox.StandardButton.NoButton, self)
         m = "You've been working on this manuscript for 738 days now. Hang in there!"
         msg_box.setInformativeText(m)
         msg_box.setDetailedText('"A long time ago in a galaxy far, far away...."')
-        msg_box.addButton("&Keep", QMessageBox.AcceptRole)
-        msg_box.addButton("Delete", QMessageBox.RejectRole)
-        if msg_box.exec() == QMessageBox.AcceptRole:
+        msg_box.addButton("&Keep", QMessageBox.ButtonRole.AcceptRole)
+        msg_box.addButton("Delete", QMessageBox.ButtonRole.RejectRole)
+        if msg_box.exec() == QMessageBox.ButtonRole.AcceptRole:
             self._warning_label.setText("Keep")
         else:
             self._warning_label.setText("Delete")
index 6be3d020b28d3cfcab41f009760fe66ddea30ae1..c8eeec6b1bf5de16414e219f32dfc4a33be1c21b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/dialogs/tabdialog example from Qt v6.x"""
 
@@ -34,7 +35,7 @@ class TabDialog(QDialog):
         tab_widget.addTab(ApplicationsTab(file_info, self), "Applications")
 
         button_box = QDialogButtonBox(
-            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
+            QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
         )
 
         button_box.accepted.connect(self.accept)
@@ -56,20 +57,20 @@ class GeneralTab(QWidget):
 
         path_label = QLabel("Path:")
         path_value_label = QLabel(file_info.absoluteFilePath())
-        path_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+        path_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
 
         size_label = QLabel("Size:")
         size = file_info.size() / 1024
         size_value_label = QLabel(f"{size} K")
-        size_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+        size_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
 
         last_read_label = QLabel("Last Read:")
         last_read_value_label = QLabel(file_info.lastRead().toString())
-        last_read_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+        last_read_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
 
         last_mod_label = QLabel("Last Modified:")
         last_mod_value_label = QLabel(file_info.lastModified().toString())
-        last_mod_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+        last_mod_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
 
         main_layout = QVBoxLayout()
         main_layout.addWidget(file_name_label)
@@ -108,11 +109,11 @@ class PermissionsTab(QWidget):
 
         owner_label = QLabel("Owner")
         owner_value_label = QLabel(file_info.owner())
-        owner_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+        owner_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
 
         group_label = QLabel("Group")
         group_value_label = QLabel(file_info.group())
-        group_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+        group_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
 
         permissions_layout = QVBoxLayout()
         permissions_layout.addWidget(readable)
diff --git a/examples/widgets/dialogs/trivialwizard/doc/trivialwizard.png b/examples/widgets/dialogs/trivialwizard/doc/trivialwizard.png
new file mode 100644 (file)
index 0000000..02291df
Binary files /dev/null and b/examples/widgets/dialogs/trivialwizard/doc/trivialwizard.png differ
diff --git a/examples/widgets/dialogs/trivialwizard/doc/trivialwizard.rst b/examples/widgets/dialogs/trivialwizard/doc/trivialwizard.rst
new file mode 100644 (file)
index 0000000..24b97b0
--- /dev/null
@@ -0,0 +1,11 @@
+Trivial Wizard Example
+======================
+
+The Trivial Wizard example illustrates how to create a linear three-page
+registration wizard using three instances of `QWizardPage` and one instance of
+`QWizard`. This example demonstrates the analogous Qt example `Trivial Wizard
+Example <https://doc.qt.io/qt-6/qtwidgets-dialogs-trivialwizard-example.html>`_.
+
+.. image:: trivialwizard.png
+    :width: 400
+    :alt: trivialwizard screenshot
index 2e551ae19bfbbaefeccc3e05a57b25106d7a4ad9..25f4f9625d37edc63005a63f0a3d803852ec8184 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/dialogs/trivialwizard example from Qt v5.x"""
 
index b929bd5e39e3305ae6e6fc5bce47986fcdc354ab..bbaa514a2ca5b171bf8304736e22840a4309e3ab 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from pathlib import Path
 import sys
@@ -13,7 +14,7 @@ class DragWidget(QFrame):
     def __init__(self, parent: QWidget):
         super().__init__(parent)
         self.setMinimumSize(200, 200)
-        self.setFrameStyle(QFrame.Sunken | QFrame.StyledPanel)
+        self.setFrameStyle(QFrame.Shadow.Sunken | QFrame.Shape.StyledPanel)
         self.setAcceptDrops(True)
 
         path = Path(__file__).resolve().parent
@@ -22,24 +23,24 @@ class DragWidget(QFrame):
         boat_icon.setPixmap(QPixmap(path / "images" / "boat.png"))
         boat_icon.move(10, 10)
         boat_icon.show()
-        boat_icon.setAttribute(Qt.WA_DeleteOnClose)
+        boat_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
 
         car_icon = QLabel(self)
         car_icon.setPixmap(QPixmap(path / "images" / "car.png"))
         car_icon.move(100, 10)
         car_icon.show()
-        car_icon.setAttribute(Qt.WA_DeleteOnClose)
+        car_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
 
         house_icon = QLabel(self)
         house_icon.setPixmap(QPixmap(path / "images" / "house.png"))
         house_icon.move(10, 80)
         house_icon.show()
-        house_icon.setAttribute(Qt.WA_DeleteOnClose)
+        house_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
 
     def dragEnterEvent(self, event):
         if event.mimeData().hasFormat("application/x-dnditem_data"):
             if event.source() == self:
-                event.setDropAction(Qt.MoveAction)
+                event.setDropAction(Qt.DropAction.MoveAction)
                 event.accept()
             else:
                 event.acceptProposedAction()
@@ -49,7 +50,7 @@ class DragWidget(QFrame):
     def dragMoveEvent(self, event):
         if event.mimeData().hasFormat("application/x-dnditem_data"):
             if event.source() == self:
-                event.setDropAction(Qt.MoveAction)
+                event.setDropAction(Qt.DropAction.MoveAction)
                 event.accept()
             else:
                 event.acceptProposedAction()
@@ -59,7 +60,7 @@ class DragWidget(QFrame):
     def dropEvent(self, event):
         if event.mimeData().hasFormat("application/x-dnditem_data"):
             item_data: QByteArray = event.mimeData().data("application/x-dnditem_data")
-            data_stream = QDataStream(item_data, QIODevice.ReadOnly)
+            data_stream = QDataStream(item_data, QIODevice.OpenModeFlag.ReadOnly)
 
             pixmap = QPixmap()
             offset = QPoint()
@@ -70,10 +71,10 @@ class DragWidget(QFrame):
             new_icon.setPixmap(pixmap)
             new_icon.move(event.position().toPoint() - offset)
             new_icon.show()
-            new_icon.setAttribute(Qt.WA_DeleteOnClose)
+            new_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
 
             if event.source() == self:
-                event.setDropAction(Qt.MoveAction)
+                event.setDropAction(Qt.DropAction.MoveAction)
                 event.accept()
             else:
                 event.acceptProposedAction()
@@ -88,7 +89,7 @@ class DragWidget(QFrame):
         pixmap = child.pixmap()
 
         item_data = QByteArray()
-        data_stream = QDataStream(item_data, QIODevice.WriteOnly)
+        data_stream = QDataStream(item_data, QIODevice.OpenModeFlag.WriteOnly)
 
         data_stream << pixmap << QPoint(event.position().toPoint() - child.pos())
 
@@ -107,7 +108,8 @@ class DragWidget(QFrame):
 
         child.setPixmap(temp_pixmap)
 
-        if drag.exec(Qt.CopyAction | Qt.MoveAction, Qt.CopyAction) == Qt.MoveAction:
+        if drag.exec(Qt.DropAction.CopyAction | Qt.DropAction.MoveAction,
+                     Qt.DropAction.CopyAction) == Qt.DropAction.MoveAction:
             child.close()
         else:
             child.show()
diff --git a/examples/widgets/draganddrop/draggabletext/doc/draggabletext.png b/examples/widgets/draganddrop/draggabletext/doc/draggabletext.png
new file mode 100644 (file)
index 0000000..8fe5ae4
Binary files /dev/null and b/examples/widgets/draganddrop/draggabletext/doc/draggabletext.png differ
diff --git a/examples/widgets/draganddrop/draggabletext/doc/draggabletext.rst b/examples/widgets/draganddrop/draggabletext/doc/draggabletext.rst
new file mode 100644 (file)
index 0000000..2da12e5
--- /dev/null
@@ -0,0 +1,8 @@
+Draggable Text Example
+======================
+
+Illustrates how to drag and drop text between widgets.
+
+.. image:: draggabletext.png
+    :width: 400
+    :alt: draggabletext screenshot
index 6ffdbd70eb0a5880b0af0bc15a09e4476e09c81a..504e6bd02cbd126c1b1be894c16cdc815023bcf7 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/draganddrop/draggabletext example from Qt v5.x,
    originating from PyQt"""
@@ -17,8 +18,8 @@ class DragLabel(QLabel):
         super().__init__(text, parent)
 
         self.setAutoFillBackground(True)
-        self.setFrameShape(QFrame.Panel)
-        self.setFrameShadow(QFrame.Raised)
+        self.setFrameShape(QFrame.Shape.Panel)
+        self.setFrameShadow(QFrame.Shadow.Raised)
 
     def mousePressEvent(self, event):
         hot_spot = event.position().toPoint()
@@ -37,9 +38,10 @@ class DragLabel(QLabel):
         drag.setPixmap(pixmap)
         drag.setHotSpot(hot_spot)
 
-        drop_action = drag.exec(Qt.CopyAction | Qt.MoveAction, Qt.CopyAction)
+        drop_action = drag.exec(Qt.DropAction.CopyAction | Qt.DropAction.MoveAction,
+                                Qt.DropAction.CopyAction)
 
-        if drop_action == Qt.MoveAction:
+        if drop_action == Qt.DropAction.MoveAction:
             self.close()
             self.update()
 
@@ -49,7 +51,7 @@ class DragWidget(QWidget):
         super().__init__(parent)
 
         dictionary_file = QFile(':/dictionary/words.txt')
-        dictionary_file.open(QIODevice.ReadOnly)
+        dictionary_file.open(QIODevice.OpenModeFlag.ReadOnly)
 
         x = 5
         y = 5
@@ -64,7 +66,7 @@ class DragWidget(QWidget):
                 y += word_label.height() + 2
 
         new_palette = self.palette()
-        new_palette.setColor(QPalette.Window, Qt.white)
+        new_palette.setColor(QPalette.ColorRole.Window, Qt.GlobalColor.white)
         self.setPalette(new_palette)
 
         self.setAcceptDrops(True)
@@ -74,7 +76,7 @@ class DragWidget(QWidget):
     def dragEnterEvent(self, event):
         if event.mimeData().hasText():
             if event.source() in self.children():
-                event.setDropAction(Qt.MoveAction)
+                event.setDropAction(Qt.DropAction.MoveAction)
                 event.accept()
             else:
                 event.acceptProposedAction()
@@ -101,7 +103,7 @@ class DragWidget(QWidget):
                 position += QPoint(new_label.width(), 0)
 
             if event.source() in self.children():
-                event.setDropAction(Qt.MoveAction)
+                event.setDropAction(Qt.DropAction.MoveAction)
                 event.accept()
             else:
                 event.acceptProposedAction()
index efc4614f0a1d0a3dc733803dbc359ee5ef30fb1f..1c10a6867cfa5237a3494e95221d3cd283068583 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QMimeData, Qt, Slot, Signal
 from PySide6.QtGui import QPalette, QPixmap
@@ -13,15 +14,15 @@ class DropArea(QLabel):
     def __init__(self, parent=None):
         super().__init__(parent)
         self.setMinimumSize(200, 200)
-        self.setFrameStyle(QFrame.Sunken | QFrame.StyledPanel)
-        self.setAlignment(Qt.AlignCenter)
+        self.setFrameStyle(QFrame.Shadow.Sunken | QFrame.Shape.StyledPanel)
+        self.setAlignment(Qt.AlignmentFlag.AlignCenter)
         self.setAcceptDrops(True)
         self.setAutoFillBackground(True)
         self.clear()
 
     def dragEnterEvent(self, event):
         self.setText("<drop content>")
-        self.setBackgroundRole(QPalette.Highlight)
+        self.setBackgroundRole(QPalette.ColorRole.Highlight)
 
         event.acceptProposedAction()
         self.changed.emit(event.mimeData())
@@ -36,13 +37,13 @@ class DropArea(QLabel):
             self.setPixmap(QPixmap(mime_data.imageData()))
         elif mime_data.hasFormat("text/markdown"):
             self.setText(mime_data.data("text/markdown"))
-            self.setTextFormat(Qt.MarkdownText)
+            self.setTextFormat(Qt.TextFormat.MarkdownText)
         elif mime_data.hasHtml():
             self.setText(mime_data.html())
-            self.setTextFormat(Qt.RichText)
+            self.setTextFormat(Qt.TextFormat.RichText)
         elif mime_data.hasText():
             self.setText(mime_data.text())
-            self.setTextFormat(Qt.PlainText)
+            self.setTextFormat(Qt.TextFormat.PlainText)
         elif mime_data.hasUrls():
             url_list = mime_data.urls()
             text = ""
@@ -52,7 +53,7 @@ class DropArea(QLabel):
         else:
             self.setText("Cannot display data")
 
-        self.setBackgroundRole(QPalette.Dark)
+        self.setBackgroundRole(QPalette.ColorRole.Dark)
         event.acceptProposedAction()
 
     def dragLeaveEvent(self, event):
@@ -62,6 +63,6 @@ class DropArea(QLabel):
     @Slot()
     def clear(self):
         self.setText("<drop content>")
-        self.setBackgroundRole(QPalette.Dark)
+        self.setBackgroundRole(QPalette.ColorRole.Dark)
 
         self.changed.emit(None)
index 5427d520d371af85d5826c84c90ea6d3311e711f..0d6bbebf3529339248e2d42c1f9a179c5b2ee44c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import re
 from PySide6.QtCore import QMimeData, Qt, Slot
@@ -40,7 +41,7 @@ class DropSiteWindow(QWidget):
 
         self._formats_table = QTableWidget()
         self._formats_table.setColumnCount(2)
-        self._formats_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
+        self._formats_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
         self._formats_table.setHorizontalHeaderLabels(["Format", "Content"])
         self._formats_table.horizontalHeader().setStretchLastSection(True)
 
@@ -49,11 +50,11 @@ class DropSiteWindow(QWidget):
         quit_button = QPushButton("Quit")
 
         button_box = QDialogButtonBox()
-        button_box.addButton(clear_button, QDialogButtonBox.ActionRole)
-        button_box.addButton(self._copy_button, QDialogButtonBox.ActionRole)
+        button_box.addButton(clear_button, QDialogButtonBox.ButtonRole.ActionRole)
+        button_box.addButton(self._copy_button, QDialogButtonBox.ButtonRole.ActionRole)
         self._copy_button.setVisible(False)
 
-        button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
+        button_box.addButton(quit_button, QDialogButtonBox.ButtonRole.RejectRole)
 
         quit_button.clicked.connect(self.close)
         clear_button.clicked.connect(drop_area.clear)
@@ -77,8 +78,8 @@ class DropSiteWindow(QWidget):
 
         for format in mime_data.formats():
             format_item = QTableWidgetItem(format)
-            format_item.setFlags(Qt.ItemIsEnabled)
-            format_item.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
+            format_item.setFlags(Qt.ItemFlag.ItemIsEnabled)
+            format_item.setTextAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft)
 
             if format == "text/plain":
                 text = simplify_whitespace(mime_data.text())
index bce476a61f03b229b06e9a3a3b816d2007fbc230..1c4365ed7d33775d780ee4420a2f220acbcf9e61 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 16c14a758e6e5d774723e9e5dea6a27da394c8f8..cb9b2f34e31c481dccd738086c02cffffe8bb978 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtCore import QRect
index 6ff11f217df857d2bab8e6b7b01ec9974c73ea1b..c42a956965b3ae8cae642da0e6ee99f6a7bf3bec 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from pathlib import Path
@@ -34,10 +35,10 @@ class BlurPicker(QGraphicsView):
         self.index = 0
 
         self._animation.setDuration(400)
-        self._animation.setEasingCurve(QEasingCurve.InOutSine)
+        self._animation.setEasingCurve(QEasingCurve.Type.InOutSine)
 
-        self.setRenderHint(QPainter.Antialiasing, True)
-        self.setFrameStyle(QFrame.NoFrame)
+        self.setRenderHint(QPainter.RenderHint.Antialiasing, True)
+        self.setFrameStyle(QFrame.Shape.NoFrame)
 
     @Property(float)
     def index(self) -> float:
@@ -85,9 +86,9 @@ class BlurPicker(QGraphicsView):
 
     def keyPressEvent(self, event):
         delta = 0
-        if event.key() == Qt.Key_Left:
+        if event.key() == Qt.Key.Key_Left:
             delta = -1
-        elif event.key() == Qt.Key_Right:
+        elif event.key() == Qt.Key.Key_Right:
             delta = 1
 
         if self._animation.state() == QAbstractAnimation.Stopped and delta:
@@ -99,7 +100,7 @@ class BlurPicker(QGraphicsView):
         right = event.position().x() > (self.width() / 2)
         delta = 1 if right else -1
 
-        if self._animation.state() == QAbstractAnimation.Stopped:
+        if self._animation.state() == QAbstractAnimation.State.Stopped:
             self._animation.setEndValue(self._index + delta)
             self._animation.start()
             event.accept()
index 053ac60ec89864d6d530bbf01f71d7f3bc62dede..7f2df0db87a77870aa05c2f56f895b992c56c7f6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/effects/blurpicker example from Qt v6.x"""
 
diff --git a/examples/widgets/effects/lighting/doc/lighting.rst b/examples/widgets/effects/lighting/doc/lighting.rst
new file mode 100644 (file)
index 0000000..95adb46
--- /dev/null
@@ -0,0 +1,11 @@
+Lighting Example
+================
+
+This example demonstrates a PySide6 application that creates a dynamic scene with lighting and
+shadow effects using `QGraphicsView` and `QGraphicsScene`. It features animated light sources and
+graphical items with drop shadows that respond to the light, showcasing advanced rendering and
+animation techniques.
+
+.. image:: lighting.webp
+    :width: 400
+    :alt: lighting screenshot
diff --git a/examples/widgets/effects/lighting/doc/lighting.webp b/examples/widgets/effects/lighting/doc/lighting.webp
new file mode 100644 (file)
index 0000000..fbae74d
Binary files /dev/null and b/examples/widgets/effects/lighting/doc/lighting.webp differ
index f074813c3c0bbb557d6b0d6b1b20e62dcf064320..2970d55a2d42b09e702fbeb47fe4c5ae4a0edd35 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 import sys
@@ -31,8 +32,8 @@ class Lighting(QGraphicsView):
         timer.setInterval(30)
         timer.start()
 
-        self.setRenderHint(QPainter.Antialiasing)
-        self.setFrameStyle(QFrame.NoFrame)
+        self.setRenderHint(QPainter.RenderHint.Antialiasing)
+        self.setFrameStyle(QFrame.Shape.NoFrame)
 
     def setup_scene(self):
         self.m_scene.setSceneRect(-300, -200, 600, 460)
@@ -43,15 +44,15 @@ class Lighting(QGraphicsView):
         self.setBackgroundBrush(linear_grad)
 
         radial_grad = QRadialGradient(30, 30, 30)
-        radial_grad.setColorAt(0, Qt.yellow)
-        radial_grad.setColorAt(0.2, Qt.yellow)
-        radial_grad.setColorAt(1, Qt.transparent)
+        radial_grad.setColorAt(0, Qt.GlobalColor.yellow)
+        radial_grad.setColorAt(0.2, Qt.GlobalColor.yellow)
+        radial_grad.setColorAt(1, Qt.GlobalColor.transparent)
 
         pixmap = QPixmap(60, 60)
-        pixmap.fill(Qt.transparent)
+        pixmap.fill(Qt.GlobalColor.transparent)
 
         with QPainter(pixmap) as painter:
-            painter.setPen(Qt.NoPen)
+            painter.setPen(Qt.PenStyle.NoPen)
             painter.setBrush(radial_grad)
             painter.drawEllipse(0, 0, 60, 60)
 
@@ -65,8 +66,8 @@ class Lighting(QGraphicsView):
                 else:
                     item = QGraphicsRectItem(0, 0, 50, 50)
 
-                item.setPen(QPen(Qt.black, 1))
-                item.setBrush(QBrush(Qt.white))
+                item.setPen(QPen(Qt.GlobalColor.black, 1))
+                item.setBrush(QBrush(Qt.GlobalColor.white))
 
                 effect = QGraphicsDropShadowEffect(self)
                 effect.setBlurRadius(8)
index ea1127fe714a3e9e247bd91871e64aede1c2f7c2..9c00591fd5b57af5aa9ebc98a71fe94ad6fbfbd7 100644 (file)
@@ -1,5 +1,3 @@
-.. _gettext-example:
-
 GNU gettext Example
 ===================
 
index 85695728db99c4669df30813c235462c86d56c3b..6710a2365779a67f17cef99f61d3f2bfd5c400cd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from pathlib import Path
 import gettext
@@ -23,7 +24,7 @@ class Window(QMainWindow):
         quit_action.triggered.connect(self.close)
 
         self._list_widget = QListWidget()
-        self._list_widget.setSelectionMode(QAbstractItemView.MultiSelection)
+        self._list_widget.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
         self._list_widget.selectionModel().selectionChanged.connect(self.selection_changed)
         self._list_widget.addItem("C++")
         self._list_widget.addItem("Java")
index 9d9be041f5ef487d9276b477cb13495a69a0d3b2..331153122af9c2569bad6dc188737ee6ccc3a4d8 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -18,7 +19,7 @@ def create_item(minimum, preferred, maximum, name):
     w.setMinimumSize(minimum)
     w.setPreferredSize(preferred)
     w.setMaximumSize(maximum)
-    w.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+    w.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
 
     return w
 
@@ -44,45 +45,45 @@ if __name__ == '__main__':
     l = QGraphicsAnchorLayout()  # noqa: E741
     l.setSpacing(0)
 
-    w = QGraphicsWidget(None, Qt.Window)
+    w = QGraphicsWidget(None, Qt.WindowType.Window)
     w.setPos(20, 20)
     w.setLayout(l)
 
-    # Vertical.
-    l.addAnchor(a, Qt.AnchorTop, l, Qt.AnchorTop)
-    l.addAnchor(b, Qt.AnchorTop, l, Qt.AnchorTop)
+    # Vertical
+    l.addAnchor(a, Qt.AnchorPoint.AnchorTop, l, Qt.AnchorPoint.AnchorTop)
+    l.addAnchor(b, Qt.AnchorPoint.AnchorTop, l, Qt.AnchorPoint.AnchorTop)
 
-    l.addAnchor(c, Qt.AnchorTop, a, Qt.AnchorBottom)
-    l.addAnchor(c, Qt.AnchorTop, b, Qt.AnchorBottom)
-    l.addAnchor(c, Qt.AnchorBottom, d, Qt.AnchorTop)
-    l.addAnchor(c, Qt.AnchorBottom, e, Qt.AnchorTop)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorTop, a, Qt.AnchorPoint.AnchorBottom)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorTop, b, Qt.AnchorPoint.AnchorBottom)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorBottom, d, Qt.AnchorPoint.AnchorTop)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorBottom, e, Qt.AnchorPoint.AnchorTop)
 
-    l.addAnchor(d, Qt.AnchorBottom, l, Qt.AnchorBottom)
-    l.addAnchor(e, Qt.AnchorBottom, l, Qt.AnchorBottom)
+    l.addAnchor(d, Qt.AnchorPoint.AnchorBottom, l, Qt.AnchorPoint.AnchorBottom)
+    l.addAnchor(e, Qt.AnchorPoint.AnchorBottom, l, Qt.AnchorPoint.AnchorBottom)
 
-    l.addAnchor(c, Qt.AnchorTop, f, Qt.AnchorTop)
-    l.addAnchor(c, Qt.AnchorVerticalCenter, f, Qt.AnchorBottom)
-    l.addAnchor(f, Qt.AnchorBottom, g, Qt.AnchorTop)
-    l.addAnchor(c, Qt.AnchorBottom, g, Qt.AnchorBottom)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorTop, f, Qt.AnchorPoint.AnchorTop)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorVerticalCenter, f, Qt.AnchorPoint.AnchorBottom)
+    l.addAnchor(f, Qt.AnchorPoint.AnchorBottom, g, Qt.AnchorPoint.AnchorTop)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorBottom, g, Qt.AnchorPoint.AnchorBottom)
 
     # Horizontal.
-    l.addAnchor(l, Qt.AnchorLeft, a, Qt.AnchorLeft)
-    l.addAnchor(l, Qt.AnchorLeft, d, Qt.AnchorLeft)
-    l.addAnchor(a, Qt.AnchorRight, b, Qt.AnchorLeft)
+    l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, a, Qt.AnchorPoint.AnchorLeft)
+    l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, d, Qt.AnchorPoint.AnchorLeft)
+    l.addAnchor(a, Qt.AnchorPoint.AnchorRight, b, Qt.AnchorPoint.AnchorLeft)
 
-    l.addAnchor(a, Qt.AnchorRight, c, Qt.AnchorLeft)
-    l.addAnchor(c, Qt.AnchorRight, e, Qt.AnchorLeft)
+    l.addAnchor(a, Qt.AnchorPoint.AnchorRight, c, Qt.AnchorPoint.AnchorLeft)
+    l.addAnchor(c, Qt.AnchorPoint.AnchorRight, e, Qt.AnchorPoint.AnchorLeft)
 
-    l.addAnchor(b, Qt.AnchorRight, l, Qt.AnchorRight)
-    l.addAnchor(e, Qt.AnchorRight, l, Qt.AnchorRight)
-    l.addAnchor(d, Qt.AnchorRight, e, Qt.AnchorLeft)
+    l.addAnchor(b, Qt.AnchorPoint.AnchorRight, l, Qt.AnchorPoint.AnchorRight)
+    l.addAnchor(e, Qt.AnchorPoint.AnchorRight, l, Qt.AnchorPoint.AnchorRight)
+    l.addAnchor(d, Qt.AnchorPoint.AnchorRight, e, Qt.AnchorPoint.AnchorLeft)
 
-    l.addAnchor(l, Qt.AnchorLeft, f, Qt.AnchorLeft)
-    l.addAnchor(l, Qt.AnchorLeft, g, Qt.AnchorLeft)
-    l.addAnchor(f, Qt.AnchorRight, g, Qt.AnchorRight)
+    l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, f, Qt.AnchorPoint.AnchorLeft)
+    l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, g, Qt.AnchorPoint.AnchorLeft)
+    l.addAnchor(f, Qt.AnchorPoint.AnchorRight, g, Qt.AnchorPoint.AnchorRight)
 
     scene.addItem(w)
-    scene.setBackgroundBrush(Qt.darkGreen)
+    scene.setBackgroundBrush(Qt.GlobalColor.darkGreen)
 
     view = QGraphicsView(scene)
     view.show()
diff --git a/examples/widgets/graphicsview/anchorlayout/doc/anchorlayout.rst b/examples/widgets/graphicsview/anchorlayout/doc/anchorlayout.rst
new file mode 100644 (file)
index 0000000..8eef329
--- /dev/null
@@ -0,0 +1,12 @@
+Anchor Layout Example
+=====================
+
+This application demonstrates the use of `QGraphicsAnchorLayout` in a PySide6 Widget application.
+It creates a graphical scene with several `QPushButton` widgets arranged using anchor constraints,
+allowing for dynamic and flexible layout management within a `QGraphicsView`. It demonstrates
+the analogous Qt example
+`Anchor Layout Example <https://doc.qt.io/qt-6.2/qtwidgets-graphicsview-anchorlayout-example.html>`_
+
+.. image:: anchorlayout.webp
+    :width: 400
+    :alt: anchorlayout screenshot
diff --git a/examples/widgets/graphicsview/anchorlayout/doc/anchorlayout.webp b/examples/widgets/graphicsview/anchorlayout/doc/anchorlayout.webp
new file mode 100644 (file)
index 0000000..cfc89c6
Binary files /dev/null and b/examples/widgets/graphicsview/anchorlayout/doc/anchorlayout.webp differ
index fb24db3ab9359f1bf992bfa5089cf1ffad28ed4e..54d256b6af97d8b53930fe65a3758748c434adc9 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 import sys
@@ -57,12 +58,12 @@ class Mouse(QGraphicsItem):
         painter.drawEllipse(-10, -20, 20, 40)
 
         # Eyes.
-        painter.setBrush(Qt.white)
+        painter.setBrush(Qt.GlobalColor.white)
         painter.drawEllipse(-10, -17, 8, 8)
         painter.drawEllipse(2, -17, 8, 8)
 
         # Nose.
-        painter.setBrush(Qt.black)
+        painter.setBrush(Qt.GlobalColor.black)
         painter.drawEllipse(QRectF(-2, -22, 4, 4))
 
         # Pupils.
@@ -71,9 +72,9 @@ class Mouse(QGraphicsItem):
 
         # Ears.
         if self.scene().collidingItems(self):
-            painter.setBrush(Qt.red)
+            painter.setBrush(Qt.GlobalColor.red)
         else:
-            painter.setBrush(Qt.darkYellow)
+            painter.setBrush(Qt.GlobalColor.darkYellow)
 
         painter.drawEllipse(-17, -12, 16, 16)
         painter.drawEllipse(1, -12, 16, 16)
@@ -83,7 +84,7 @@ class Mouse(QGraphicsItem):
         path.cubicTo(-5, 22, -5, 22, 0, 25)
         path.cubicTo(5, 27, 5, 32, 0, 30)
         path.cubicTo(-5, 32, -5, 42, 0, 35)
-        painter.setBrush(Qt.NoBrush)
+        painter.setBrush(Qt.BrushStyle.NoBrush)
         painter.drawPath(path)
 
     def advance(self, phase):
@@ -154,7 +155,7 @@ if __name__ == '__main__':
 
     scene = QGraphicsScene()
     scene.setSceneRect(-300, -300, 600, 600)
-    scene.setItemIndexMethod(QGraphicsScene.NoIndex)
+    scene.setItemIndexMethod(QGraphicsScene.ItemIndexMethod.NoIndex)
 
     for i in range(MOUSE_COUNT):
         mouse = Mouse()
@@ -163,11 +164,11 @@ if __name__ == '__main__':
         scene.addItem(mouse)
 
     view = QGraphicsView(scene)
-    view.setRenderHint(QPainter.Antialiasing)
+    view.setRenderHint(QPainter.RenderHint.Antialiasing)
     view.setBackgroundBrush(QBrush(QPixmap(':/images/cheese.jpg')))
-    view.setCacheMode(QGraphicsView.CacheBackground)
-    view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
-    view.setDragMode(QGraphicsView.ScrollHandDrag)
+    view.setCacheMode(QGraphicsView.CacheModeFlag.CacheBackground)
+    view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
+    view.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
     view.setWindowTitle("Colliding Mice")
     view.resize(400, 300)
     view.show()
diff --git a/examples/widgets/graphicsview/collidingmice/doc/collidingmice.rst b/examples/widgets/graphicsview/collidingmice/doc/collidingmice.rst
new file mode 100644 (file)
index 0000000..4490159
--- /dev/null
@@ -0,0 +1,15 @@
+Colliding Mice Example
+======================
+
+This application, is a graphical simulation built using PySide6. It creates a
+scene with multiple animated mouse objects that move around and interact with
+each other within a `QGraphicsView`. The mice change direction to avoid
+collisions and exhibit random movements, demonstrating the use of
+`QGraphicsItem`, `QGraphicsScene`, and `QGraphicsView` for creating dynamic and
+interactive graphics in a PySide6 application. This example demonstrates the
+analogous Qt example `Colliding Mice Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-collidingmice-example.html>`_.
+
+.. image:: collidingmice.webp
+    :width: 400
+    :alt: collidingmice screenshot
diff --git a/examples/widgets/graphicsview/collidingmice/doc/collidingmice.webp b/examples/widgets/graphicsview/collidingmice/doc/collidingmice.webp
new file mode 100644 (file)
index 0000000..aafd2b1
Binary files /dev/null and b/examples/widgets/graphicsview/collidingmice/doc/collidingmice.webp differ
index e2a22b2e4dd50c130fbe47962fc0ff811244d28f..0fe2f902260dcd53a4bb40a0de574c2e6c33edcd 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # Resource object code
 #
index 60e05613ccc6bbb6c3657ec29c889c102ce1e490..317113afefa12d2355a32e9696e2fce002835cf8 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 import sys
@@ -29,7 +30,7 @@ class Arrow(QGraphicsLineItem):
         self._my_start_item = startItem
         self._my_end_item = endItem
         self.setFlag(QGraphicsItem.ItemIsSelectable, True)
-        self._my_color = Qt.black
+        self._my_color = Qt.GlobalColor.black
         self.setPen(QPen(self._my_color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
 
     def set_color(self, color):
@@ -121,22 +122,22 @@ class DiagramTextItem(QGraphicsTextItem):
     def __init__(self, parent=None, scene=None):
         super().__init__(parent, scene)
 
-        self.setFlag(QGraphicsItem.ItemIsMovable)
-        self.setFlag(QGraphicsItem.ItemIsSelectable)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
 
     def itemChange(self, change, value):
-        if change == QGraphicsItem.ItemSelectedChange:
+        if change == QGraphicsItem.GraphicsItemChange.ItemSelectedChange:
             self.selected_change.emit(self)
         return value
 
     def focusOutEvent(self, event):
-        self.setTextInteractionFlags(Qt.NoTextInteraction)
+        self.setTextInteractionFlags(Qt.TextInteractionFlag.NoTextInteraction)
         self.lost_focus.emit(self)
         super(DiagramTextItem, self).focusOutEvent(event)
 
     def mouseDoubleClickEvent(self, event):
-        if self.textInteractionFlags() == Qt.NoTextInteraction:
-            self.setTextInteractionFlags(Qt.TextEditorInteraction)
+        if self.textInteractionFlags() == Qt.TextInteractionFlag.NoTextInteraction:
+            self.setTextInteractionFlags(Qt.TextInteractionFlag.TextEditorInteraction)
         super(DiagramTextItem, self).mouseDoubleClickEvent(event)
 
 
@@ -177,8 +178,8 @@ class DiagramItem(QGraphicsPolygonItem):
                 QPointF(-120, -80)])
 
         self.setPolygon(self._my_polygon)
-        self.setFlag(QGraphicsItem.ItemIsMovable, True)
-        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, True)
 
     def remove_arrow(self, arrow):
         try:
@@ -197,9 +198,9 @@ class DiagramItem(QGraphicsPolygonItem):
 
     def image(self):
         pixmap = QPixmap(250, 250)
-        pixmap.fill(Qt.transparent)
+        pixmap.fill(Qt.GlobalColor.transparent)
         with QPainter(pixmap) as painter:
-            painter.setPen(QPen(Qt.black, 8))
+            painter.setPen(QPen(Qt.GlobalColor.black, 8))
             painter.translate(125, 125)
             painter.drawPolyline(self._my_polygon)
         return pixmap
@@ -210,7 +211,7 @@ class DiagramItem(QGraphicsPolygonItem):
         self._my_context_menu.exec(event.screenPos())
 
     def itemChange(self, change, value):
-        if change == QGraphicsItem.ItemPositionChange:
+        if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange:
             for arrow in self.arrows:
                 arrow.updatePosition()
 
@@ -234,9 +235,9 @@ class DiagramScene(QGraphicsScene):
         self._my_item_type = DiagramItem.Step
         self.line = None
         self._text_item = None
-        self._my_item_color = Qt.white
-        self._my_text_color = Qt.black
-        self._my_line_color = Qt.black
+        self._my_item_color = Qt.GlobalColor.white
+        self._my_text_color = Qt.GlobalColor.black
+        self._my_line_color = Qt.GlobalColor.black
         self._my_font = QFont()
 
     def set_line_color(self, color):
@@ -280,7 +281,7 @@ class DiagramScene(QGraphicsScene):
             item.deleteLater()
 
     def mousePressEvent(self, mouseEvent):
-        if (mouseEvent.button() != Qt.LeftButton):
+        if (mouseEvent.button() != Qt.MouseButton.LeftButton):
             return
 
         if self._my_mode == self.InsertItem:
@@ -296,7 +297,7 @@ class DiagramScene(QGraphicsScene):
         elif self._my_mode == self.InsertText:
             text_item = DiagramTextItem()
             text_item.setFont(self._my_font)
-            text_item.setTextInteractionFlags(Qt.TextEditorInteraction)
+            text_item.setTextInteractionFlags(Qt.TextInteractionFlag.TextEditorInteraction)
             text_item.setZValue(1000.0)
             text_item.lost_focus.connect(self.editor_lost_focus)
             text_item.selected_change.connect(self.item_selected)
@@ -529,7 +530,7 @@ class MainWindow(QMainWindow):
         font = item.font()
         self._font_combo.setCurrentFont(font)
         self._font_size_combo.setEditText(str(font.pointSize()))
-        self._bold_action.setChecked(font.weight() == QFont.Bold)
+        self._bold_action.setChecked(font.weight() == QFont.Weight.Bold)
         self._italic_action.setChecked(font.italic())
         self._underline_action.setChecked(font.underline())
 
@@ -556,8 +557,8 @@ class MainWindow(QMainWindow):
         text_button.setIconSize(QSize(50, 50))
 
         text_layout = QGridLayout()
-        text_layout.addWidget(text_button, 0, 0, Qt.AlignHCenter)
-        text_layout.addWidget(QLabel("Text"), 1, 0, Qt.AlignCenter)
+        text_layout.addWidget(text_button, 0, 0, Qt.AlignmentFlag.AlignHCenter)
+        text_layout.addWidget(QLabel("Text"), 1, 0, Qt.AlignmentFlag.AlignCenter)
         text_widget = QWidget()
         text_widget.setLayout(text_layout)
         layout.addWidget(text_widget, 1, 1)
@@ -588,7 +589,8 @@ class MainWindow(QMainWindow):
         background_widget.setLayout(background_layout)
 
         self._tool_box = QToolBox()
-        self._tool_box.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Ignored))
+        self._tool_box.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Maximum,
+                                     QSizePolicy.Policy.Ignored))
         self._tool_box.setMinimumWidth(item_widget.sizeHint().width())
         self._tool_box.addItem(item_widget, "Basic Flowchart Shapes")
         self._tool_box.addItem(background_widget, "Backgrounds")
@@ -658,31 +660,31 @@ class MainWindow(QMainWindow):
         self._font_size_combo.currentIndexChanged.connect(self.font_size_changed)
 
         self._font_color_tool_button = QToolButton()
-        self._font_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
+        self._font_color_tool_button.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
         self._font_color_tool_button.setMenu(
-            self.create_color_menu(self.text_color_changed, Qt.black))
+            self.create_color_menu(self.text_color_changed, Qt.GlobalColor.black))
         self._text_action = self._font_color_tool_button.menu().defaultAction()
         self._font_color_tool_button.setIcon(
-            self.create_color_tool_button_icon(':/images/textpointer.png', Qt.black))
+            self.create_color_tool_button_icon(':/images/textpointer.png', Qt.GlobalColor.black))
         self._font_color_tool_button.setAutoFillBackground(True)
         self._font_color_tool_button.clicked.connect(self.text_button_triggered)
 
         self._fill_color_tool_button = QToolButton()
-        self._fill_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
+        self._fill_color_tool_button.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
         self._fill_color_tool_button.setMenu(
-            self.create_color_menu(self.item_color_changed, Qt.white))
+            self.create_color_menu(self.item_color_changed, Qt.GlobalColor.white))
         self._fill_action = self._fill_color_tool_button.menu().defaultAction()
         self._fill_color_tool_button.setIcon(
-            self.create_color_tool_button_icon(':/images/floodfill.png', Qt.white))
+            self.create_color_tool_button_icon(':/images/floodfill.png', Qt.GlobalColor.white))
         self._fill_color_tool_button.clicked.connect(self.fill_button_triggered)
 
         self._line_color_tool_button = QToolButton()
-        self._line_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
+        self._line_color_tool_button.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
         self._line_color_tool_button.setMenu(
-            self.create_color_menu(self.line_color_changed, Qt.black))
+            self.create_color_menu(self.line_color_changed, Qt.GlobalColor.black))
         self._line_action = self._line_color_tool_button.menu().defaultAction()
         self._line_color_tool_button.setIcon(
-            self.create_color_tool_button_icon(':/images/linecolor.png', Qt.black))
+            self.create_color_tool_button_icon(':/images/linecolor.png', Qt.GlobalColor.black))
         self._line_color_tool_button.clicked.connect(self.line_button_triggered)
 
         self._text_tool_bar = self.addToolBar("Font")
@@ -729,8 +731,8 @@ class MainWindow(QMainWindow):
         self._background_button_group.addButton(button)
 
         layout = QGridLayout()
-        layout.addWidget(button, 0, 0, Qt.AlignHCenter)
-        layout.addWidget(QLabel(text), 1, 0, Qt.AlignCenter)
+        layout.addWidget(button, 0, 0, Qt.AlignmentFlag.AlignHCenter)
+        layout.addWidget(QLabel(text), 1, 0, Qt.AlignmentFlag.AlignCenter)
 
         widget = QWidget()
         widget.setLayout(layout)
@@ -748,8 +750,8 @@ class MainWindow(QMainWindow):
         self._button_group.addButton(button, diagram_type)
 
         layout = QGridLayout()
-        layout.addWidget(button, 0, 0, Qt.AlignHCenter)
-        layout.addWidget(QLabel(text), 1, 0, Qt.AlignCenter)
+        layout.addWidget(button, 0, 0, Qt.AlignmentFlag.AlignHCenter)
+        layout.addWidget(QLabel(text), 1, 0, Qt.AlignmentFlag.AlignCenter)
 
         widget = QWidget()
         widget.setLayout(layout)
@@ -757,7 +759,8 @@ class MainWindow(QMainWindow):
         return widget
 
     def create_color_menu(self, slot, defaultColor):
-        colors = [Qt.black, Qt.white, Qt.red, Qt.blue, Qt.yellow]
+        colors = [Qt.GlobalColor.black, Qt.GlobalColor.white, Qt.GlobalColor.red,
+                  Qt.GlobalColor.blue, Qt.GlobalColor.yellow]
         names = ["black", "white", "red", "blue", "yellow"]
 
         color_menu = QMenu(self)
@@ -771,7 +774,7 @@ class MainWindow(QMainWindow):
 
     def create_color_tool_button_icon(self, imageFile, color):
         pixmap = QPixmap(50, 80)
-        pixmap.fill(Qt.transparent)
+        pixmap.fill(Qt.GlobalColor.transparent)
 
         with QPainter(pixmap) as painter:
             image = QPixmap(imageFile)
@@ -786,7 +789,7 @@ class MainWindow(QMainWindow):
         pixmap = QPixmap(20, 20)
 
         with QPainter(pixmap) as painter:
-            painter.setPen(Qt.NoPen)
+            painter.setPen(Qt.PenStyle.NoPen)
             painter.fillRect(QRect(0, 0, 20, 20), color)
 
         return QIcon(pixmap)
diff --git a/examples/widgets/graphicsview/diagramscene/doc/diagramscene.rst b/examples/widgets/graphicsview/diagramscene/doc/diagramscene.rst
new file mode 100644 (file)
index 0000000..828e14a
--- /dev/null
@@ -0,0 +1,11 @@
+Diagram Scene Example
+=====================
+
+This example demonstrates how to create an interactive diagram editor using
+PySide6, featuring various diagram shapes, connectors, and customizable
+properties. It demonstrates the analogous Qt example `Diagram Scene Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-diagramscene-example.html>`_.
+
+.. image:: diagramscene.webp
+    :width: 400
+    :alt: diagramscene screenshot
diff --git a/examples/widgets/graphicsview/diagramscene/doc/diagramscene.webp b/examples/widgets/graphicsview/diagramscene/doc/diagramscene.webp
new file mode 100644 (file)
index 0000000..e94a839
Binary files /dev/null and b/examples/widgets/graphicsview/diagramscene/doc/diagramscene.webp differ
diff --git a/examples/widgets/graphicsview/dragdroprobot/doc/dragdroprobot.png b/examples/widgets/graphicsview/dragdroprobot/doc/dragdroprobot.png
new file mode 100644 (file)
index 0000000..b8eff6f
Binary files /dev/null and b/examples/widgets/graphicsview/dragdroprobot/doc/dragdroprobot.png differ
diff --git a/examples/widgets/graphicsview/dragdroprobot/doc/dragdroprobot.rst b/examples/widgets/graphicsview/dragdroprobot/doc/dragdroprobot.rst
new file mode 100644 (file)
index 0000000..f2e6b7e
--- /dev/null
@@ -0,0 +1,12 @@
+Drag and Drop Robot Example
+===========================
+
+This example demonstrates a PySide6 application where users can drag and drop
+color items onto a robot graphic, showcasing interactive drag-and-drop
+functionality within a `QGraphicsView`. It demonstrates the analogous Qt example
+`Drag and Drop Robot Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-dragdroprobot-example.html>`_.
+
+.. image:: dragdroprobot.png
+    :width: 400
+    :alt: dragdroprobot screenshot
index 9ed92b26e6fc08cd61b682ba697ca2e80e2fcdc6..c25de9131788c48994ef522d0946f312a65a5e71 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import math
@@ -31,29 +32,29 @@ class ColorItem(QGraphicsItem):
         (r, g, b) = (self.color.red(), self.color.green(), self.color.blue())
         self.setToolTip(
             f"QColor({r}, {g}, {b})\nClick and drag this color onto the robot!")
-        self.setCursor(Qt.OpenHandCursor)
+        self.setCursor(Qt.CursorShape.OpenHandCursor)
         self._start_drag_distance = QApplication.startDragDistance()
 
     def boundingRect(self):
         return QRectF(-15.5, -15.5, 34, 34)
 
     def paint(self, painter, option, widget):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.darkGray)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.darkGray)
         painter.drawEllipse(-12, -12, 30, 30)
-        painter.setPen(QPen(Qt.black, 1))
+        painter.setPen(QPen(Qt.GlobalColor.black, 1))
         painter.setBrush(QBrush(self.color))
         painter.drawEllipse(-15, -15, 30, 30)
 
     def mousePressEvent(self, event):
-        if event.button() != Qt.LeftButton:
+        if event.button() != Qt.MouseButton.LeftButton:
             event.ignore()
             return
 
-        self.setCursor(Qt.ClosedHandCursor)
+        self.setCursor(Qt.CursorShape.ClosedHandCursor)
 
     def mouseMoveEvent(self, event):
-        start = QPointF(event.buttonDownScreenPos(Qt.LeftButton))
+        start = QPointF(event.buttonDownScreenPos(Qt.MouseButton.LeftButton))
         if QLineF(event.screenPos(), start).length() < self._start_drag_distance:
             return
 
@@ -73,11 +74,11 @@ class ColorItem(QGraphicsItem):
             mime.setText(f"#{r:02x}{g:02x}{b:02x}")
 
             pixmap = QPixmap(34, 34)
-            pixmap.fill(Qt.white)
+            pixmap.fill(Qt.GlobalColor.white)
 
             with QPainter(pixmap) as painter:
                 painter.translate(15, 15)
-                painter.setRenderHint(QPainter.Antialiasing)
+                painter.setRenderHint(QPainter.RenderHint.Antialiasing)
                 self.paint(painter, None, None)
 
             pixmap.setMask(pixmap.createHeuristicMask())
@@ -86,17 +87,17 @@ class ColorItem(QGraphicsItem):
             drag.setHotSpot(QPoint(15, 20))
 
         drag.exec()
-        self.setCursor(Qt.OpenHandCursor)
+        self.setCursor(Qt.CursorShape.OpenHandCursor)
 
     def mouseReleaseEvent(self, event):
-        self.setCursor(Qt.OpenHandCursor)
+        self.setCursor(Qt.CursorShape.OpenHandCursor)
 
 
 class RobotPart(QGraphicsItem):
     def __init__(self, parent=None):
         super().__init__(parent)
 
-        self.color = QColor(Qt.lightGray)
+        self.color = QColor(Qt.GlobalColor.lightGray)
         self.pixmap = None
         self._drag_over = False
 
@@ -132,15 +133,15 @@ class RobotHead(RobotPart):
     def paint(self, painter, option, widget=None):
         if not self.pixmap:
             painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
-            painter.drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt.RelativeSize)
-            painter.setBrush(Qt.white)
+            painter.drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt.SizeMode.RelativeSize)
+            painter.setBrush(Qt.GlobalColor.white)
             painter.drawEllipse(-7, -3 - 20, 7, 7)
             painter.drawEllipse(0, -3 - 20, 7, 7)
-            painter.setBrush(Qt.black)
+            painter.setBrush(Qt.GlobalColor.black)
             painter.drawEllipse(-5, -1 - 20, 2, 2)
             painter.drawEllipse(2, -1 - 20, 2, 2)
-            painter.setPen(QPen(Qt.black, 2))
-            painter.setBrush(Qt.NoBrush)
+            painter.setPen(QPen(Qt.GlobalColor.black, 2))
+            painter.setBrush(Qt.BrushStyle.NoBrush)
             painter.drawArc(-6, -2 - 20, 12, 15, 190 * 16, 160 * 16)
         else:
             painter.scale(.2272, .2824)
@@ -154,7 +155,7 @@ class RobotTorso(RobotPart):
     def paint(self, painter, option, widget=None):
         painter.setBrush(self._drag_over and self.color.lighter(130)
                          or self.color)
-        painter.drawRoundedRect(-20, -20, 40, 60, 25, 25, Qt.RelativeSize)
+        painter.drawRoundedRect(-20, -20, 40, 60, 25, 25, Qt.SizeMode.RelativeSize)
         painter.drawEllipse(-25, -20, 20, 20)
         painter.drawEllipse(5, -20, 20, 20)
         painter.drawEllipse(-20, 22, 20, 20)
@@ -168,7 +169,7 @@ class RobotLimb(RobotPart):
     def paint(self, painter, option, widget=None):
         painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
         painter.drawRoundedRect(self.boundingRect(), 50, 50,
-                                Qt.RelativeSize)
+                                Qt.SizeMode.RelativeSize)
         painter.drawEllipse(-5, -5, 10, 10)
 
 
@@ -214,7 +215,7 @@ class Robot(RobotPart):
         self.animations[0].setScaleAt(1, 1.1, 1.1)
 
         self.timeline.setUpdateInterval(1000 / 25)
-        curve = QEasingCurve(QEasingCurve.SineCurve)
+        curve = QEasingCurve(QEasingCurve.Type.SineCurve)
         self.timeline.setEasingCurve(curve)
         self.timeline.setLoopCount(0)
         self.timeline.setDuration(2000)
@@ -244,8 +245,8 @@ if __name__ == '__main__':
     scene.addItem(robot)
 
     view = QGraphicsView(scene)
-    view.setRenderHint(QPainter.Antialiasing)
-    view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+    view.setRenderHint(QPainter.RenderHint.Antialiasing)
+    view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
     view.setBackgroundBrush(QColor(230, 200, 167))
     view.setWindowTitle("Drag and Drop Robot")
     view.show()
diff --git a/examples/widgets/graphicsview/elasticnodes/doc/elasticnodes.png b/examples/widgets/graphicsview/elasticnodes/doc/elasticnodes.png
new file mode 100644 (file)
index 0000000..840f74f
Binary files /dev/null and b/examples/widgets/graphicsview/elasticnodes/doc/elasticnodes.png differ
diff --git a/examples/widgets/graphicsview/elasticnodes/doc/elasticnodes.rst b/examples/widgets/graphicsview/elasticnodes/doc/elasticnodes.rst
new file mode 100644 (file)
index 0000000..91afe0b
--- /dev/null
@@ -0,0 +1,15 @@
+Elastic Nodes Example
+=====================
+
+This example demonstrates a PySide6 application that creates a dynamic and
+interactive graphical scene with nodes connected by elastic edges. The nodes
+and edges respond to user interactions and physics-based movements, showcasing
+the use of `QGraphicsItem`, `QGraphicsScene`, and `QGraphicsView` for creating
+complex animations and interactions.
+
+It demonstrates the analogous Qt example `Elastic Nodes Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-elasticnodes-example.html>`_.
+
+.. image:: elasticnodes.png
+    :width: 400
+    :alt: elasticnodes screenshot
index 90cb496269b426b2bfff1b54bca363fd91e73def..3a051bd8e6738b8509e97d024e9516f7521c49fb 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import weakref
@@ -26,7 +27,7 @@ class Edge(QGraphicsItem):
         self._arrow_size = 10.0
         self._source_point = QPointF()
         self._dest_point = QPointF()
-        self.setAcceptedMouseButtons(Qt.NoButton)
+        self.setAcceptedMouseButtons(Qt.MouseButton.NoButton)
         self.source = weakref.ref(sourceNode)
         self.dest = weakref.ref(destNode)
         self.source().add_edge(self)
@@ -89,7 +90,8 @@ class Edge(QGraphicsItem):
         if line.length() == 0.0:
             return
 
-        painter.setPen(QPen(Qt.black, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
+        painter.setPen(QPen(Qt.GlobalColor.black, 1, Qt.PenStyle.SolidLine,
+                            Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))
         painter.drawLine(line)
 
         # Draw the arrows if there's enough room.
@@ -111,7 +113,7 @@ class Edge(QGraphicsItem):
                               math.cos(angle - math.pi + math.pi / 3) * self._arrow_size)
         dest_arrow_p2 = self._dest_point + arrow_head2
 
-        painter.setBrush(Qt.black)
+        painter.setBrush(Qt.GlobalColor.black)
         painter.drawPolygon(QPolygonF([line.p1(), source_arrow_p1, source_arrow_p2]))
         painter.drawPolygon(QPolygonF([line.p2(), dest_arrow_p1, dest_arrow_p2]))
 
@@ -124,9 +126,9 @@ class Node(QGraphicsItem):
         self.graph = weakref.ref(graphWidget)
         self._edge_list = []
         self._new_pos = QPointF()
-        self.setFlag(QGraphicsItem.ItemIsMovable)
-        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
-        self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
+        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges)
+        self.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
         self.setZValue(-1)
 
     def item_type(self):
@@ -197,26 +199,26 @@ class Node(QGraphicsItem):
         return path
 
     def paint(self, painter, option, widget):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.darkGray)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.darkGray)
         painter.drawEllipse(-7, -7, 20, 20)
 
         gradient = QRadialGradient(-3, -3, 10)
-        if option.state & QStyle.State_Sunken:
+        if option.state & QStyle.StateFlag.State_Sunken:
             gradient.setCenter(3, 3)
             gradient.setFocalPoint(3, 3)
-            gradient.setColorAt(1, QColor(Qt.yellow).lighter(120))
-            gradient.setColorAt(0, QColor(Qt.darkYellow).lighter(120))
+            gradient.setColorAt(1, QColor(Qt.GlobalColor.yellow).lighter(120))
+            gradient.setColorAt(0, QColor(Qt.GlobalColor.darkYellow).lighter(120))
         else:
-            gradient.setColorAt(0, Qt.yellow)
-            gradient.setColorAt(1, Qt.darkYellow)
+            gradient.setColorAt(0, Qt.GlobalColor.yellow)
+            gradient.setColorAt(1, Qt.GlobalColor.darkYellow)
 
         painter.setBrush(QBrush(gradient))
-        painter.setPen(QPen(Qt.black, 0))
+        painter.setPen(QPen(Qt.GlobalColor.black, 0))
         painter.drawEllipse(-10, -10, 20, 20)
 
     def itemChange(self, change, value):
-        if change == QGraphicsItem.ItemPositionChange:
+        if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange:
             for edge in self._edge_list:
                 edge().adjust()
             self.graph().item_moved()
@@ -239,13 +241,13 @@ class GraphWidget(QGraphicsView):
         self._timer_id = 0
 
         scene = QGraphicsScene(self)
-        scene.setItemIndexMethod(QGraphicsScene.NoIndex)
+        scene.setItemIndexMethod(QGraphicsScene.ItemIndexMethod.NoIndex)
         scene.setSceneRect(-200, -200, 400, 400)
         self.setScene(scene)
-        self.setCacheMode(QGraphicsView.CacheBackground)
-        self.setRenderHint(QPainter.Antialiasing)
-        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
-        self.setResizeAnchor(QGraphicsView.AnchorViewCenter)
+        self.setCacheMode(QGraphicsView.CacheModeFlag.CacheBackground)
+        self.setRenderHint(QPainter.RenderHint.Antialiasing)
+        self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
+        self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorViewCenter)
 
         node1 = Node(self)
         node2 = Node(self)
@@ -299,19 +301,19 @@ class GraphWidget(QGraphicsView):
     def keyPressEvent(self, event):
         key = event.key()
 
-        if key == Qt.Key_Up:
+        if key == Qt.Key.Key_Up:
             self._center_node.moveBy(0, -20)
-        elif key == Qt.Key_Down:
+        elif key == Qt.Key.Key_Down:
             self._center_node.moveBy(0, 20)
-        elif key == Qt.Key_Left:
+        elif key == Qt.Key.Key_Left:
             self._center_node.moveBy(-20, 0)
-        elif key == Qt.Key_Right:
+        elif key == Qt.Key.Key_Right:
             self._center_node.moveBy(20, 0)
-        elif key == Qt.Key_Plus:
+        elif key == Qt.Key.Key_Plus:
             self.scale_view(1.2)
-        elif key == Qt.Key_Minus:
+        elif key == Qt.Key.Key_Minus:
             self.scale_view(1 / 1.2)
-        elif key == Qt.Key_Space or key == Qt.Key_Enter:
+        elif key == Qt.Key.Key_Space or key == Qt.Key.Key_Enter:
             for item in self.scene().items():
                 if isinstance(item, Node):
                     item.setPos(-150 + random(300), -150 + random(300))
@@ -351,7 +353,7 @@ class GraphWidget(QGraphicsView):
 
         # Fill.
         gradient = QLinearGradient(scene_rect.topLeft(), scene_rect.bottomRight())
-        gradient.setColorAt(0, Qt.white)
+        gradient.setColorAt(0, Qt.GlobalColor.white)
         gradient.setColorAt(1, Qt.lightGray)
         painter.fillRect(rect.intersected(scene_rect), QBrush(gradient))
         painter.setBrush(Qt.NoBrush)
@@ -369,7 +371,7 @@ class GraphWidget(QGraphicsView):
         painter.setFont(font)
         painter.setPen(Qt.lightGray)
         painter.drawText(text_rect.translated(2, 2), message)
-        painter.setPen(Qt.black)
+        painter.setPen(Qt.GlobalColor.black)
         painter.drawText(text_rect, message)
 
     def scale_view(self, scaleFactor):
diff --git a/examples/widgets/imageviewer/doc/imageviewer.rst b/examples/widgets/imageviewer/doc/imageviewer.rst
new file mode 100644 (file)
index 0000000..5a30017
--- /dev/null
@@ -0,0 +1,12 @@
+Image Viewer Example
+====================
+
+This example demonstrates an image viewer application built using PySide6,
+featuring functionalities such as opening, saving, printing, copying, pasting,
+and zooming images within a scrollable area. This example demonstrates the
+analogous Qt example `Image Viewer Example
+<https://doc.qt.io/qt-6.2/qtwidgets-widgets-imageviewer-example.html>`_.
+
+.. image:: imageviewer.webp
+    :width: 400
+    :alt: imageviewer screenshot
diff --git a/examples/widgets/imageviewer/doc/imageviewer.webp b/examples/widgets/imageviewer/doc/imageviewer.webp
new file mode 100644 (file)
index 0000000..ecc2f6a
Binary files /dev/null and b/examples/widgets/imageviewer/doc/imageviewer.webp differ
index 93b1e87ba16724650fbed2b5ec4af156a5dd63a5..eb0e1788ef65b9dde563e4e977d517c88fc702b1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtPrintSupport import QPrintDialog, QPrinter
 from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog, QLabel,
@@ -32,13 +33,13 @@ class ImageViewer(QMainWindow):
         self._scale_factor = 1.0
         self._first_file_dialog = True
         self._image_label = QLabel()
-        self._image_label.setBackgroundRole(QPalette.Base)
-        self._image_label.setSizePolicy(QSizePolicy.Ignored,
-                                        QSizePolicy.Ignored)
+        self._image_label.setBackgroundRole(QPalette.ColorRole.Base)
+        self._image_label.setSizePolicy(QSizePolicy.Policy.Ignored,
+                                        QSizePolicy.Policy.Ignored)
         self._image_label.setScaledContents(True)
 
         self._scroll_area = QScrollArea()
-        self._scroll_area.setBackgroundRole(QPalette.Dark)
+        self._scroll_area.setBackgroundRole(QPalette.ColorRole.Dark)
         self._scroll_area.setWidget(self._image_label)
         self._scroll_area.setVisible(False)
         self.setCentralWidget(self._scroll_area)
@@ -72,7 +73,8 @@ class ImageViewer(QMainWindow):
     def _set_image(self, new_image):
         self._image = new_image
         if self._image.colorSpace().isValid():
-            self._image.convertToColorSpace(QColorSpace.SRgb)
+            color_space = QColorSpace(QColorSpace.NamedColorSpace.SRgb)
+            self._image.convertToColorSpace(color_space)
         self._image_label.setPixmap(QPixmap.fromImage(self._image))
         self._scale_factor = 1.0
 
@@ -100,16 +102,16 @@ class ImageViewer(QMainWindow):
     @Slot()
     def _open(self):
         dialog = QFileDialog(self, "Open File")
-        self._initialize_image_filedialog(dialog, QFileDialog.AcceptOpen)
-        while (dialog.exec() == QDialog.Accepted
+        self._initialize_image_filedialog(dialog, QFileDialog.AcceptMode.AcceptOpen)
+        while (dialog.exec() == QDialog.DialogCode.Accepted
                and not self.load_file(dialog.selectedFiles()[0])):
             pass
 
     @Slot()
     def _save_as(self):
         dialog = QFileDialog(self, "Save File As")
-        self._initialize_image_filedialog(dialog, QFileDialog.AcceptSave)
-        while (dialog.exec() == QDialog.Accepted
+        self._initialize_image_filedialog(dialog, QFileDialog.AcceptMode.AcceptSave)
+        while (dialog.exec() == QDialog.DialogCode.Accepted
                and not self._save_file(dialog.selectedFiles()[0])):
             pass
 
@@ -117,7 +119,7 @@ class ImageViewer(QMainWindow):
     def _print_(self):
         printer = QPrinter()
         dialog = QPrintDialog(printer, self)
-        if dialog.exec() == QDialog.Accepted:
+        if dialog.exec() == QDialog.DialogCode.Accepted:
             with QPainter(printer) as painter:
                 pixmap = self._image_label.pixmap()
                 rect = painter.viewport()
@@ -175,7 +177,7 @@ class ImageViewer(QMainWindow):
 
         self._open_act = file_menu.addAction("&Open...")
         self._open_act.triggered.connect(self._open)
-        self._open_act.setShortcut(QKeySequence.Open)
+        self._open_act.setShortcut(QKeySequence.StandardKey.Open)
 
         self._save_as_act = file_menu.addAction("&Save As...")
         self._save_as_act.triggered.connect(self._save_as)
@@ -183,7 +185,7 @@ class ImageViewer(QMainWindow):
 
         self._print_act = file_menu.addAction("&Print...")
         self._print_act.triggered.connect(self._print_)
-        self._print_act.setShortcut(QKeySequence.Print)
+        self._print_act.setShortcut(QKeySequence.StandardKey.Print)
         self._print_act.setEnabled(False)
 
         file_menu.addSeparator()
@@ -196,23 +198,23 @@ class ImageViewer(QMainWindow):
 
         self._copy_act = edit_menu.addAction("&Copy")
         self._copy_act.triggered.connect(self._copy)
-        self._copy_act.setShortcut(QKeySequence.Copy)
+        self._copy_act.setShortcut(QKeySequence.StandardKey.Copy)
         self._copy_act.setEnabled(False)
 
         self._paste_act = edit_menu.addAction("&Paste")
         self._paste_act.triggered.connect(self._paste)
-        self._paste_act.setShortcut(QKeySequence.Paste)
+        self._paste_act.setShortcut(QKeySequence.StandardKey.Paste)
 
         view_menu = self.menuBar().addMenu("&View")
 
         self._zoom_in_act = view_menu.addAction("Zoom &In (25%)")
-        self._zoom_in_act.setShortcut(QKeySequence.ZoomIn)
+        self._zoom_in_act.setShortcut(QKeySequence.StandardKey.ZoomIn)
         self._zoom_in_act.triggered.connect(self._zoom_in)
         self._zoom_in_act.setEnabled(False)
 
         self._zoom_out_act = view_menu.addAction("Zoom &Out (25%)")
         self._zoom_out_act.triggered.connect(self._zoom_out)
-        self._zoom_out_act.setShortcut(QKeySequence.ZoomOut)
+        self._zoom_out_act.setShortcut(QKeySequence.StandardKey.ZoomOut)
         self._zoom_out_act.setEnabled(False)
 
         self._normal_size_act = view_menu.addAction("&Normal Size")
@@ -263,7 +265,7 @@ class ImageViewer(QMainWindow):
     def _initialize_image_filedialog(self, dialog, acceptMode):
         if self._first_file_dialog:
             self._first_file_dialog = False
-            locations = QStandardPaths.standardLocations(QStandardPaths.PicturesLocation)
+            locations = QStandardPaths.standardLocations(QStandardPaths.StandardLocation.PicturesLocation)  # noqa: E501
             directory = locations[-1] if locations else QDir.currentPath()
             dialog.setDirectory(directory)
 
@@ -273,5 +275,5 @@ class ImageViewer(QMainWindow):
         dialog.setMimeTypeFilters(mime_types)
         dialog.selectMimeTypeFilter("image/jpeg")
         dialog.setAcceptMode(acceptMode)
-        if acceptMode == QFileDialog.AcceptSave:
+        if acceptMode == QFileDialog.AcceptMode.AcceptSave:
             dialog.setDefaultSuffix("jpg")
index 949def62ea08bbb0c6c9e4908723216ab7b5b6be..2af5833aa38b8c6ce70538d22716c884b558fd92 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/imageviewer example from Qt v6.0"""
 
index ecb853e804477a3bd472947614a91c8fb134a52d..cd850bed092b9770f1727977dd67f298ae76fd41 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt
 from PySide6.QtWidgets import (QDialog, QLabel, QTextEdit, QLineEdit,
@@ -15,8 +16,8 @@ class AddDialogWidget(QDialog):
 
         name_label = QLabel("Name")
         address_label = QLabel("Address")
-        button_box = QDialogButtonBox(QDialogButtonBox.Ok
-                                      | QDialogButtonBox.Cancel)
+        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+                                      | QDialogButtonBox.StandardButton.Cancel)
 
         self._name_text = QLineEdit()
         self._address_text = QTextEdit()
@@ -25,8 +26,8 @@ class AddDialogWidget(QDialog):
         grid.setColumnStretch(1, 2)
         grid.addWidget(name_label, 0, 0)
         grid.addWidget(self._name_text, 0, 1)
-        grid.addWidget(address_label, 1, 0, Qt.AlignLeft | Qt.AlignTop)
-        grid.addWidget(self._address_text, 1, 1, Qt.AlignLeft)
+        grid.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
+        grid.addWidget(self._address_text, 1, 1, Qt.AlignmentFlag.AlignLeft)
 
         layout = QVBoxLayout()
         layout.addLayout(grid)
index af0cf3dee38103c5641ed3dec8ea1d7a69666b86..647326903cbe794771c982e95ba6deb64bc13958 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtGui import QAction
index cb2f46ea1bf7394e488508be05939e54d4717fd9..37bd06f85548925129a224a2714c186d34a46cd5 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 try:
     import cpickle as pickle
@@ -67,11 +68,11 @@ class AddressWidget(QTabWidget):
             # Step 2: get the index of the newly created row and use it.
             # to set the name
             ix = self._table_model.index(0, 0, QModelIndex())
-            self._table_model.setData(ix, address["name"], Qt.EditRole)
+            self._table_model.setData(ix, address["name"], Qt.ItemDataRole.EditRole)
 
             # Step 3: lather, rinse, repeat for the address.
             ix = self._table_model.index(0, 1, QModelIndex())
-            self._table_model.setData(ix, address["address"], Qt.EditRole)
+            self._table_model.setData(ix, address["address"], Qt.ItemDataRole.EditRole)
 
             # Remove the newAddressTab, as we now have at least one
             # address in the model.
@@ -98,9 +99,9 @@ class AddressWidget(QTabWidget):
 
         row = proxy_model.mapToSource(indexes[0]).row()
         ix = self._table_model.index(row, 0, QModelIndex())
-        name = self._table_model.data(ix, Qt.DisplayRole)
+        name = self._table_model.data(ix, Qt.ItemDataRole.DisplayRole)
         ix = self._table_model.index(row, 1, QModelIndex())
-        address = self._table_model.data(ix, Qt.DisplayRole)
+        address = self._table_model.data(ix, Qt.ItemDataRole.DisplayRole)
 
         # Open an addDialogWidget, and only allow the user to edit the address.
         add_dialog = AddDialogWidget()
@@ -115,7 +116,7 @@ class AddressWidget(QTabWidget):
             new_address = add_dialog.address
             if new_address != address:
                 ix = self._table_model.index(row, 1, QModelIndex())
-                self._table_model.setData(ix, new_address, Qt.EditRole)
+                self._table_model.setData(ix, new_address, Qt.ItemDataRole.EditRole)
 
     @Slot()
     def remove_entry(self):
@@ -148,11 +149,11 @@ class AddressWidget(QTabWidget):
             table_view = QTableView()
             table_view.setModel(proxy_model)
             table_view.setSortingEnabled(True)
-            table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
+            table_view.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
             table_view.horizontalHeader().setStretchLastSection(True)
             table_view.verticalHeader().hide()
-            table_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
-            table_view.setSelectionMode(QAbstractItemView.SingleSelection)
+            table_view.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+            table_view.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
 
             # This here be the magic: we use the group name (e.g. "ABC") to
             # build the regex for the QSortFilterProxyModel for the group's
@@ -161,10 +162,10 @@ class AddressWidget(QTabWidget):
             # "A", "B", or "C". Notice that we set it to be case-insensitive.
             re = QRegularExpression(f"^[{group}].*")
             assert re.isValid()
-            re.setPatternOptions(QRegularExpression.CaseInsensitiveOption)
+            re.setPatternOptions(QRegularExpression.PatternOption.CaseInsensitiveOption)
             proxy_model.setFilterRegularExpression(re)
             proxy_model.setFilterKeyColumn(0)  # Filter on the "name" column
-            proxy_model.sort(0, Qt.AscendingOrder)
+            proxy_model.sort(0, Qt.SortOrder.AscendingOrder)
 
             # This prevents an application crash (see:
             # https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash)  # noqa: E501
index d3d037ad4e6b0c29c1fb119f001b5d7f59b29922..41410176496859ecfd220a22fbb2c5946a488934 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2011 Arun Srinivasan  <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import (Qt, Signal)
 from PySide6.QtWidgets import (QWidget, QLabel, QPushButton, QVBoxLayout)
@@ -25,7 +26,7 @@ class NewAddressTab(QWidget):
 
         layout = QVBoxLayout()
         layout.addWidget(description_label)
-        layout.addWidget(add_button, 0, Qt.AlignCenter)
+        layout.addWidget(add_button, 0, Qt.AlignmentFlag.AlignCenter)
 
         self.setLayout(layout)
 
index 3c1dbd4cc3ba5c1b9bf71b5c20996cb621aa74c0..6654edfad5829c52da2391938bbbfb49097192c4 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import (Qt, QAbstractTableModel, QModelIndex)
 
@@ -23,7 +24,7 @@ class TableModel(QAbstractTableModel):
         """ Returns the number of columns the model holds. """
         return 2
 
-    def data(self, index, role=Qt.DisplayRole):
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
         """ Depending on the index and role given, return data. If not
             returning data, return None (PySide equivalent of QT's
             "invalid QVariant").
@@ -34,7 +35,7 @@ class TableModel(QAbstractTableModel):
         if not 0 <= index.row() < len(self.addresses):
             return None
 
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             name = self.addresses[index.row()]["name"]
             address = self.addresses[index.row()]["address"]
 
@@ -45,12 +46,12 @@ class TableModel(QAbstractTableModel):
 
         return None
 
-    def headerData(self, section, orientation, role=Qt.DisplayRole):
+    def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
         """ Set the headers to be displayed. """
-        if role != Qt.DisplayRole:
+        if role != Qt.ItemDataRole.DisplayRole:
             return None
 
-        if orientation == Qt.Horizontal:
+        if orientation == Qt.Orientation.Horizontal:
             if section == 0:
                 return "Name"
             elif section == 1:
@@ -77,11 +78,11 @@ class TableModel(QAbstractTableModel):
         self.endRemoveRows()
         return True
 
-    def setData(self, index, value, role=Qt.EditRole):
+    def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
         """ Adjust the data (set it to <value>) depending on the given
             index and role.
         """
-        if role != Qt.EditRole:
+        if role != Qt.ItemDataRole.EditRole:
             return False
 
         if index.isValid() and 0 <= index.row() < len(self.addresses):
@@ -104,6 +105,6 @@ class TableModel(QAbstractTableModel):
             manually adjust each tableView to have NoEditTriggers.
         """
         if not index.isValid():
-            return Qt.ItemIsEnabled
+            return Qt.ItemFlag.ItemIsEnabled
         return Qt.ItemFlags(QAbstractTableModel.flags(self, index)
-                            | Qt.ItemIsEditable)
+                            | Qt.ItemFlag.ItemIsEditable)
index a30b0abdfc7b7baf0a4fe4c01eeeb9b072260891..759f1ae19f7c1053eaeade12bcf2856b678d3f51 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import (QDate, QDateTime, QRegularExpression,
@@ -91,7 +92,7 @@ class Window(QWidget):
         self.setWindowTitle("Basic Sort/Filter Model")
         self.resize(500, 450)
 
-        self._proxy_view.sortByColumn(1, Qt.AscendingOrder)
+        self._proxy_view.sortByColumn(1, Qt.SortOrder.AscendingOrder)
         self._filter_column_combo_box.setCurrentIndex(1)
 
         self._filter_pattern_line_edit.setText("Andy|Grace")
@@ -114,7 +115,7 @@ class Window(QWidget):
         reg_exp = QRegularExpression(pattern)
         if not self._filter_case_sensitivity_check_box.isChecked():
             options = reg_exp.patternOptions()
-            options |= QRegularExpression.CaseInsensitiveOption
+            options |= QRegularExpression.PatternOption.CaseInsensitiveOption
             reg_exp.setPatternOptions(options)
         self._proxy_model.setFilterRegularExpression(reg_exp)
 
@@ -125,9 +126,9 @@ class Window(QWidget):
     @Slot()
     def sort_changed(self):
         if self._sort_case_sensitivity_check_box.isChecked():
-            case_sensitivity = Qt.CaseSensitive
+            case_sensitivity = Qt.CaseSensitivity.CaseSensitive
         else:
-            case_sensitivity = Qt.CaseInsensitive
+            case_sensitivity = Qt.CaseSensitivity.CaseInsensitive
 
         self._proxy_model.setSortCaseSensitivity(case_sensitivity)
 
@@ -142,9 +143,9 @@ def add_mail(model, subject, sender, date):
 def create_mail_model(parent):
     model = QStandardItemModel(0, 3, parent)
 
-    model.setHeaderData(0, Qt.Horizontal, "Subject")
-    model.setHeaderData(1, Qt.Horizontal, "Sender")
-    model.setHeaderData(2, Qt.Horizontal, "Date")
+    model.setHeaderData(0, Qt.Orientation.Horizontal, "Subject")
+    model.setHeaderData(1, Qt.Orientation.Horizontal, "Sender")
+    model.setHeaderData(2, Qt.Orientation.Horizontal, "Date")
 
     add_mail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
              QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
diff --git a/examples/widgets/itemviews/basicfiltermodel/doc/basicfiltermodel.rst b/examples/widgets/itemviews/basicfiltermodel/doc/basicfiltermodel.rst
new file mode 100644 (file)
index 0000000..a8aa5f3
--- /dev/null
@@ -0,0 +1,11 @@
+Basic Sort/Filter Model Example
+===============================
+
+The Basic Sort/Filter Model example illustrates how to use `QSortFilterProxyModel`
+to perform basic sorting and filtering. This example demonstrates the analogous
+Qt example `Basic Sort/Filter Model Example
+<https://doc.qt.io/qt-6/qtwidgets-itemviews-basicsortfiltermodel-example.html>`_.
+
+.. image:: basicfiltermodel.webp
+    :width: 400
+    :alt: basicsortfiltermodel screenshot
diff --git a/examples/widgets/itemviews/basicfiltermodel/doc/basicfiltermodel.webp b/examples/widgets/itemviews/basicfiltermodel/doc/basicfiltermodel.webp
new file mode 100644 (file)
index 0000000..6d290cb
Binary files /dev/null and b/examples/widgets/itemviews/basicfiltermodel/doc/basicfiltermodel.webp differ
index d1be6958e7f61314da817e0f5d2abc0957a2212d..5408f5c7f36839c7d0f237ec1c52df6369300400 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2020 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from argparse import ArgumentParser, RawTextHelpFormatter
index c936972a076b67e043dd4ca1036a225d0e2a2f1d..b5c7c240904afb71a805d7140eb322a95ac43cf9 100644 (file)
@@ -7,4 +7,3 @@ A Python application that demonstrates the analogous example in C++
 .. image:: editabletreemodel.png
    :width: 611
    :alt: editabletreemodel screenshot
-
index 491baa68d518848706774ac8f28cbd53ecc01026..dd33c542431f1b4128aaff104bfcc8b459d830a9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 import sys
index 1489bf28b5492554e6bea17e90d03b1a4da69d41..e33220e8c4545c4f0d7772329171a3b8c14f6070 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from pathlib import Path
@@ -20,8 +21,8 @@ class MainWindow(QMainWindow):
 
         self.view = QTreeView()
         self.view.setAlternatingRowColors(True)
-        self.view.setSelectionBehavior(QAbstractItemView.SelectItems)
-        self.view.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
+        self.view.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectItems)
+        self.view.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
         self.view.setAnimated(False)
         self.view.setAllColumnsShowFocus(True)
         self.setCentralWidget(self.view)
@@ -90,13 +91,13 @@ class MainWindow(QMainWindow):
 
         for column in range(model.columnCount(index)):
             child: QModelIndex = model.index(0, column, index)
-            model.setData(child, "[No data]", Qt.EditRole)
-            if not model.headerData(column, Qt.Horizontal):
-                model.setHeaderData(column, Qt.Horizontal, "[No header]",
-                                    Qt.EditRole)
+            model.setData(child, "[No data]", Qt.ItemDataRole.EditRole)
+            if not model.headerData(column, Qt.Orientation.Horizontal):
+                model.setHeaderData(column, Qt.Orientation.Horizontal, "[No header]",
+                                    Qt.ItemDataRole.EditRole)
 
         selection_model.setCurrentIndex(
-            model.index(0, 0, index), QItemSelectionModel.ClearAndSelect
+            model.index(0, 0, index), QItemSelectionModel.SelectionFlag.ClearAndSelect
         )
         self.update_actions()
 
@@ -107,8 +108,8 @@ class MainWindow(QMainWindow):
 
         changed: bool = model.insertColumn(column + 1)
         if changed:
-            model.setHeaderData(column + 1, Qt.Horizontal, "[No header]",
-                                Qt.EditRole)
+            model.setHeaderData(column + 1, Qt.Orientation.Horizontal, "[No header]",
+                                Qt.ItemDataRole.EditRole)
 
         self.update_actions()
 
@@ -125,7 +126,7 @@ class MainWindow(QMainWindow):
 
         for column in range(model.columnCount(parent)):
             child: QModelIndex = model.index(index.row() + 1, column, parent)
-            model.setData(child, "[No data]", Qt.EditRole)
+            model.setData(child, "[No data]", Qt.ItemDataRole.EditRole)
 
     @Slot()
     def remove_column(self) -> None:
index 1a25b0774460f3ffb45a535fe3033a0ca138209a..dfc98b3e82b028ffe1ccdf3b75a16f7700aeeed7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 class TreeItem:
index a58572fca5ba26574f87c710809fd1fea76afa4e..98e4d4752283bf1addf8912ea15f6b13a5464404 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 
 from PySide6.QtCore import QModelIndex, Qt, QAbstractItemModel
@@ -22,7 +23,7 @@ class TreeModel(QAbstractItemModel):
         if not index.isValid():
             return None
 
-        if role != Qt.DisplayRole and role != Qt.EditRole:
+        if role != Qt.ItemDataRole.DisplayRole and role != Qt.ItemDataRole.EditRole:
             return None
 
         item: TreeItem = self.get_item(index)
@@ -31,9 +32,9 @@ class TreeModel(QAbstractItemModel):
 
     def flags(self, index: QModelIndex) -> Qt.ItemFlags:
         if not index.isValid():
-            return Qt.NoItemFlags
+            return Qt.ItemFlag.NoItemFlags
 
-        return Qt.ItemIsEditable | QAbstractItemModel.flags(self, index)
+        return Qt.ItemFlag.ItemIsEditable | QAbstractItemModel.flags(self, index)
 
     def get_item(self, index: QModelIndex = QModelIndex()) -> TreeItem:
         if index.isValid():
@@ -44,8 +45,8 @@ class TreeModel(QAbstractItemModel):
         return self.root_item
 
     def headerData(self, section: int, orientation: Qt.Orientation,
-                   role: int = Qt.DisplayRole):
-        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+                   role: int = Qt.ItemDataRole.DisplayRole):
+        if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
             return self.root_item.data(section)
 
         return None
@@ -132,20 +133,21 @@ class TreeModel(QAbstractItemModel):
         return parent_item.child_count()
 
     def setData(self, index: QModelIndex, value, role: int) -> bool:
-        if role != Qt.EditRole:
+        if role != Qt.ItemDataRole.EditRole:
             return False
 
         item: TreeItem = self.get_item(index)
         result: bool = item.set_data(index.column(), value)
 
         if result:
-            self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole])
+            self.dataChanged.emit(index, index,
+                                  [Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.EditRole])
 
         return result
 
     def setHeaderData(self, section: int, orientation: Qt.Orientation, value,
                       role: int = None) -> bool:
-        if role != Qt.EditRole or orientation != Qt.Horizontal:
+        if role != Qt.ItemDataRole.EditRole or orientation != Qt.Orientation.Horizontal:
             return False
 
         result: bool = self.root_item.set_data(section, value)
diff --git a/examples/widgets/itemviews/fetchmore/doc/fetchmore.png b/examples/widgets/itemviews/fetchmore/doc/fetchmore.png
new file mode 100644 (file)
index 0000000..e3b47b8
Binary files /dev/null and b/examples/widgets/itemviews/fetchmore/doc/fetchmore.png differ
diff --git a/examples/widgets/itemviews/fetchmore/doc/fetchmore.rst b/examples/widgets/itemviews/fetchmore/doc/fetchmore.rst
new file mode 100644 (file)
index 0000000..08fa60a
--- /dev/null
@@ -0,0 +1,10 @@
+Fetch More Example
+==================
+
+The Fetch More example shows how to add items to an item view model on demand.
+This example demonstrates the analogous Qt example `Fetch More Example
+<https://doc.qt.io/qt-6/qtwidgets-itemviews-fetchmore-example.html>`_.
+
+.. image:: fetchmore.png
+    :width: 400
+    :alt: fetchmore screenshot
index 5150250e0a9ae288cd9b25837595e98f7066f825..8f41910aa442d37efa781969fb260883cf83f276 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the itemviews/fetchmore/fetchmore example from Qt v6.x
 
@@ -36,7 +37,7 @@ class FileListModel(QAbstractListModel):
     def rowCount(self, parent=QModelIndex()):
         return self._file_count
 
-    def data(self, index, role=Qt.DisplayRole):
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
         if not index.isValid():
             return None
 
@@ -44,15 +45,15 @@ class FileListModel(QAbstractListModel):
         if row >= len(self._file_list) or row < 0:
             return None
 
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             return self._file_list[row].fileName()
 
-        if role == Qt.BackgroundRole:
+        if role == Qt.ItemDataRole.BackgroundRole:
             batch = row // BATCH_SIZE
             palette = qApp.palette()  # noqa: F821
             return palette.base() if batch % 2 == 0 else palette.alternateBase()
 
-        if role == Qt.DecorationRole:
+        if role == Qt.ItemDataRole.DecorationRole:
             return self._icon_provider.icon(self._file_list[row])
 
         return None
@@ -80,8 +81,8 @@ class FileListModel(QAbstractListModel):
         directory = QDir(path)
 
         self.beginResetModel()
-        directory_filter = QDir.AllEntries | QDir.NoDot
-        self._file_list = directory.entryInfoList(directory_filter, QDir.Name)
+        directory_filter = QDir.Filter.AllEntries | QDir.Filter.NoDot
+        self._file_list = directory.entryInfoList(directory_filter, QDir.SortFlag.Name)
         self._file_count = 0
         self.endResetModel()
 
@@ -100,8 +101,8 @@ class Window(QWidget):
         self._view.setModel(self._model)
 
         self._log_viewer = QPlainTextEdit()
-        self._log_viewer.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,
-                                       QSizePolicy.Preferred))
+        self._log_viewer.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Preferred,
+                                       QSizePolicy.Policy.Preferred))
 
         self._model.number_populated.connect(self.update_log)
         self._view.activated.connect(self.activated)
index 6e614c77fb17b9f10a9a2b7d13571d62b7e723eb..00ec1478b7cdf33a22d704ab764142ae5aa55201 100644 (file)
@@ -1,9 +1,10 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import json
 import sys
-from typing import Any, List, Dict, Union
+from typing import Any
 
 from PySide6.QtWidgets import QTreeView, QApplication, QHeaderView
 from PySide6.QtCore import QAbstractItemModel, QModelIndex, QObject, Qt, QFileInfo
@@ -71,7 +72,7 @@ class TreeItem:
 
     @classmethod
     def load(
-        cls, value: Union[List, Dict], parent: "TreeItem" = None, sort=True
+        cls, value: list | dict, parent: "TreeItem" = None, sort=True
     ) -> "TreeItem":
         """Create a 'root' TreeItem from a nested list or a nested dictonary
 
@@ -155,14 +156,14 @@ class JsonModel(QAbstractItemModel):
 
         item = index.internalPointer()
 
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             if index.column() == 0:
                 return item.key
 
             if index.column() == 1:
                 return item.value
 
-        elif role == Qt.EditRole:
+        elif role == Qt.ItemDataRole.EditRole:
             if index.column() == 1:
                 return item.value
 
@@ -177,12 +178,12 @@ class JsonModel(QAbstractItemModel):
             role (Qt.ItemDataRole)
 
         """
-        if role == Qt.EditRole:
+        if role == Qt.ItemDataRole.EditRole:
             if index.column() == 1:
                 item = index.internalPointer()
                 item.value = str(value)
 
-                self.dataChanged.emit(index, index, [Qt.EditRole])
+                self.dataChanged.emit(index, index, [Qt.ItemDataRole.EditRole])
 
                 return True
 
@@ -196,10 +197,10 @@ class JsonModel(QAbstractItemModel):
         For the JsonModel, it returns only data for columns (orientation = Horizontal)
 
         """
-        if role != Qt.DisplayRole:
+        if role != Qt.ItemDataRole.DisplayRole:
             return None
 
-        if orientation == Qt.Horizontal:
+        if orientation == Qt.Orientation.Horizontal:
             return self._headers[section]
 
     def index(self, row: int, column: int, parent=QModelIndex()) -> QModelIndex:
@@ -270,7 +271,7 @@ class JsonModel(QAbstractItemModel):
         flags = super(JsonModel, self).flags(index)
 
         if index.column() == 1:
-            return Qt.ItemIsEditable | flags
+            return Qt.ItemFlag.ItemIsEditable | flags
         else:
             return flags
 
@@ -314,7 +315,7 @@ if __name__ == "__main__":
         model.load(document)
 
     view.show()
-    view.header().setSectionResizeMode(0, QHeaderView.Stretch)
+    view.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
     view.setAlternatingRowColors(True)
     view.resize(500, 300)
     app.exec()
index 577f0faa503610cc2a8a99e78b3fd049d79bd263..11bdeddb29e5014aaa2ff04e6346b7b9d962f15b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -32,7 +33,7 @@ class SpinBoxDelegate(QStyledItemDelegate):
 
 #! [2]
     def setEditorData(self, editor, index):
-        value = index.model().data(index, Qt.EditRole)
+        value = index.model().data(index, Qt.ItemDataRole.EditRole)
         editor.setValue(value)
 #! [2]
 
@@ -40,7 +41,7 @@ class SpinBoxDelegate(QStyledItemDelegate):
     def setModelData(self, editor, model, index):
         editor.interpretText()
         value = editor.value()
-        model.setData(index, value, Qt.EditRole)
+        model.setData(index, value, Qt.ItemDataRole.EditRole)
 #! [3]
 
 #! [4]
index 0ecc5ec2343f8d458b75faee89142869ae426028..e811f62913eb263d39b1d5c51656b1773019efdf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -14,6 +15,6 @@ if __name__ == "__main__":
     sheet = SpreadSheet(10, 6)
     sheet.setWindowIcon(QPixmap(":/images/interview.png"))
     sheet.show()
-    sheet.layout().setSizeConstraint(QLayout.SetFixedSize)
+    sheet.layout().setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
 
     sys.exit(app.exec())
index 82ebe5ebbf8c6d96f15904c079f3b23cf0cf1889..88357ae14e075f2ab7d2513f10abf3bebb5e8c8d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QPoint, Qt, QCoreApplication, Slot
 from PySide6.QtGui import QAction, QBrush, QPixmap, QColor, QPainter
@@ -11,12 +12,11 @@ from PySide6.QtWidgets import (QColorDialog, QComboBox, QDialog, QFontDialog,
 from spreadsheetdelegate import SpreadSheetDelegate
 from spreadsheetitem import SpreadSheetItem
 
-from typing import Optional
 from numbers import Number
 
 
 class SpreadSheet(QMainWindow):
-    def __init__(self, rows: Number, cols: Number, parent: Optional[QWidget] = None) -> None:
+    def __init__(self, rows: Number, cols: Number, parent: QWidget | None = None) -> None:
         super().__init__(parent)
 
         self._tool_bar = QToolBar(self)
@@ -76,30 +76,30 @@ class SpreadSheet(QMainWindow):
         self._cell_sum_action.triggered.connect(self.action_sum)
 
         self._cell_add_action = QAction("&Add", self)
-        self._cell_add_action.setShortcut(Qt.CTRL | Qt.Key_Plus)
+        self._cell_add_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Plus)
         self._cell_add_action.triggered.connect(self.action_add)
 
         self._cell_sub_action = QAction("&Subtract", self)
-        self._cell_sub_action.setShortcut(Qt.CTRL | Qt.Key_Minus)
+        self._cell_sub_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Minus)
         self._cell_sub_action.triggered.connect(self.action_subtract)
 
         self._cell_mul_action = QAction("&Multiply", self)
-        self._cell_mul_action.setShortcut(Qt.CTRL | Qt.Key_multiply)
+        self._cell_mul_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_multiply)
         self._cell_mul_action.triggered.connect(self.action_multiply)
 
         self._cell_div_action = QAction("&Divide", self)
-        self._cell_div_action.setShortcut(Qt.CTRL | Qt.Key_division)
+        self._cell_div_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_division)
         self._cell_div_action.triggered.connect(self.action_divide)
 
         self._font_action = QAction("Font...", self)
-        self._font_action.setShortcut(Qt.CTRL | Qt.Key_F)
+        self._font_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_F)
         self._font_action.triggered.connect(self.select_font)
 
         self._color_action = QAction(QPixmap(16, 16), "Background &Color...", self)
         self._color_action.triggered.connect(self.select_color)
 
         self._clear_action = QAction("Clear", self)
-        self._clear_action.setShortcut(Qt.Key_Delete)
+        self._clear_action.setShortcut(Qt.Key.Key_Delete)
         self._clear_action.triggered.connect(self.clear)
 
         self._about_spreadsheet = QAction("About Spreadsheet", self)
@@ -137,7 +137,7 @@ class SpreadSheet(QMainWindow):
     @Slot(QTableWidgetItem)
     def update_status(self, item: QTableWidgetItem) -> None:
         if item and item == self._table.currentItem():
-            self.statusBar().showMessage(str(item.data(Qt.StatusTipRole)), 1000)
+            self.statusBar().showMessage(str(item.data(Qt.ItemDataRole.StatusTipRole)), 1000)
             self._cell_label.setText(
                 "Cell: ({})".format(
                     SpreadSheetItem.encode_pos(self._table.row(item), self._table.column(item))
@@ -174,7 +174,7 @@ class SpreadSheet(QMainWindow):
         if item != self._table.currentItem():
             return
         if item:
-            self._formula_input.setText(str(item.data(Qt.EditRole)))
+            self._formula_input.setText(str(item.data(Qt.ItemDataRole.EditRole)))
         else:
             self._formula_input.clear()
 
@@ -187,7 +187,7 @@ class SpreadSheet(QMainWindow):
         if not item:
             self._table.setItem(row, col, SpreadSheetItem(text))
         else:
-            item.setData(Qt.EditRole, text)
+            item.setData(Qt.ItemDataRole.EditRole, text)
         self._table.viewport().update()
 
     @Slot()
@@ -248,7 +248,7 @@ class SpreadSheet(QMainWindow):
         cell1_col_input.setCurrentIndex(c1_col)
 
         operator_label = QLabel(opText, group)
-        operator_label.setAlignment(Qt.AlignHCenter)
+        operator_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
 
         cell2_label = QLabel(c2Text, group)
         cell2_row_input = QComboBox(group)
@@ -260,7 +260,7 @@ class SpreadSheet(QMainWindow):
         cell2_col_input.setCurrentIndex(c2_col)
 
         equals_label = QLabel("=", group)
-        equals_label.setAlignment(Qt.AlignHCenter)
+        equals_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
 
         out_label = QLabel(outText, group)
         out_row_input = QComboBox(group)
@@ -405,10 +405,10 @@ class SpreadSheet(QMainWindow):
         self.addAction(self._font_action)
         self.addAction(self._second_separator)
         self.addAction(self._clear_action)
-        self.setContextMenuPolicy(Qt.ActionsContextMenu)
+        self.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
 
     def setup_contents(self) -> None:
-        title_background = QBrush(Qt.lightGray)
+        title_background = QBrush(Qt.GlobalColor.lightGray)
         title_font = self._table.font()
         title_font.setBold(True)
 
@@ -469,7 +469,7 @@ class SpreadSheet(QMainWindow):
         self._table.setItem(8, 2, SpreadSheetItem("1240"))
 
         self._table.setItem(9, 2, SpreadSheetItem())
-        self._table.item(9, 2).setBackground(Qt.lightGray)
+        self._table.item(9, 2).setBackground(Qt.GlobalColor.lightGray)
 
         # column 3
         self._table.setItem(0, 3, SpreadSheetItem("Currency"))
@@ -487,7 +487,7 @@ class SpreadSheet(QMainWindow):
         self._table.setItem(8, 3, SpreadSheetItem("USD"))
 
         self._table.setItem(9, 3, SpreadSheetItem())
-        self._table.item(9, 3).setBackground(Qt.lightGray)
+        self._table.item(9, 3).setBackground(Qt.GlobalColor.lightGray)
 
         # column 4
         self._table.setItem(0, 4, SpreadSheetItem("Ex. Rate"))
index 57aba6f4767e6ce58de77152c69a347e35466164..a6d81223f75a4bf0aeffb57f82ed9ceb2e260534 100644 (file)
@@ -1,16 +1,15 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import (QAbstractItemModel, QDate, QModelIndex, QObject,
                             QStringListModel, Qt, Slot)
 from PySide6.QtWidgets import (QCompleter, QDateTimeEdit, QLineEdit,
                                QStyleOptionViewItem, QStyledItemDelegate, QWidget)
 
-from typing import Optional
-
 
 class SpreadSheetDelegate(QStyledItemDelegate):
-    def __init__(self, parent: Optional[QObject] = None) -> None:
+    def __init__(self, parent: QObject | None = None) -> None:
         super().__init__(parent)
 
     def create_editor(self, parent: QWidget,
@@ -27,7 +26,8 @@ class SpreadSheetDelegate(QStyledItemDelegate):
         # create a completer with the strings in the column as model
         allStrings = QStringListModel()
         for i in range(1, index.model().rowCount()):
-            strItem = str(index.model().data(index.sibling(i, index.column()), Qt.EditRole))
+            strItem = str(index.model().data(index.sibling(i, index.column()),
+                                             Qt.ItemDataRole.EditRole))
 
             if not allStrings.contains(strItem):
                 allStrings.append(strItem)
@@ -46,14 +46,14 @@ class SpreadSheetDelegate(QStyledItemDelegate):
     def set_editor_data(self, editor: QWidget, index: QModelIndex) -> None:
         edit = QLineEdit(editor)
         if edit:
-            edit.setText(str(index.model().data(index, Qt.EditRole)))
+            edit.setText(str(index.model().data(index, Qt.ItemDataRole.EditRole)))
             return
 
         dateEditor = QDateTimeEdit(editor)
         if dateEditor:
             dateEditor.setDate(
                 QDate.fromString(
-                    str(index.model().data(index, Qt.EditRole)), "d/M/yyyy"))
+                    str(index.model().data(index, Qt.ItemDataRole.EditRole)), "d/M/yyyy"))
 
     def set_model_data(self, editor: QWidget,
                        model: QAbstractItemModel, index: QModelIndex) -> None:
index dc70da883b06796a35f7d121dd226140204c64b3..8f1f9b42833ed7019af65e027105fd3cac32105c 100644 (file)
@@ -1,7 +1,8 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
-from typing import Any, Tuple
+from typing import Any
 from PySide6.QtCore import QMetaType, Qt
 from PySide6.QtWidgets import QTableWidget, QTableWidgetItem
 
@@ -13,25 +14,25 @@ class SpreadSheetItem(QTableWidgetItem):
         return super().__init_subclass__()
 
     def data(self, role: int) -> Any:
-        if role == Qt.EditRole or role == Qt.StatusTipRole:
+        if role == Qt.ItemDataRole.EditRole or role == Qt.ItemDataRole.StatusTipRole:
             return self.formula()
 
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             return self.display()
 
         t = str(self.display())
 
-        if role == Qt.ForegroundRole:
+        if role == Qt.ItemDataRole.ForegroundRole:
             try:
                 number = int(t)
-                color = Qt.red if number < 0 else Qt.blue
+                color = Qt.GlobalColor.red if number < 0 else Qt.GlobalColor.blue
             except ValueError:
-                color = Qt.black
+                color = Qt.GlobalColor.black
             return color
 
-        if role == Qt.TextAlignmentRole:
+        if role == Qt.ItemDataRole.TextAlignmentRole:
             if t and (t[0].isdigit() or t[0] == '-'):
-                return int(Qt.AlignRight | Qt.AlignVCenter)
+                return int(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
 
         return super().data(role)
 
@@ -51,7 +52,7 @@ class SpreadSheetItem(QTableWidgetItem):
         return result
 
     def formula(self) -> None:
-        return str(super().data(Qt.DisplayRole))
+        return str(super().data(Qt.ItemDataRole.DisplayRole))
 
     def compute_formula(self, formula: str, widget: QTableWidget, this) -> QMetaType.Type.QVariant:
         # check if the string is actually a formula or not
@@ -106,7 +107,7 @@ class SpreadSheetItem(QTableWidgetItem):
 
         return result
 
-    def decode_pos(pos: str) -> Tuple[int, int]:
+    def decode_pos(pos: str) -> tuple[int, int]:
         if (not pos):
             col = -1
             row = -1
index 973eb14f63d84cfba73b0dfd60c1e270efd1d0b1..93b09cdff488bf5342175ad9ffd0315767d6ffae 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QStyledItemDelegate, QStyle
 
@@ -33,7 +34,7 @@ class StarDelegate(QStyledItemDelegate):
 
             # If the row is currently selected, we need to make sure we
             # paint the background accordingly.
-            if option.state & QStyle.State_Selected:
+            if option.state & QStyle.StateFlag.State_Selected:
                 # The original C++ example used option.palette.foreground() to
                 # get the brush for painting, but there are a couple of
                 # problems with that:
@@ -111,9 +112,9 @@ if __name__ == "__main__":
     # Create and populate the tableWidget
     table_widget = QTableWidget(4, 4)
     table_widget.setItemDelegate(StarDelegate())
-    table_widget.setEditTriggers(QAbstractItemView.DoubleClicked
-                                 | QAbstractItemView.SelectedClicked)
-    table_widget.setSelectionBehavior(QAbstractItemView.SelectRows)
+    table_widget.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked
+                                 | QAbstractItemView.EditTrigger.SelectedClicked)
+    table_widget.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
     table_widget.setHorizontalHeaderLabels(["Title", "Genre", "Artist", "Rating"])
 
     data = [["Mass in B-Minor", "Baroque", "J.S. Bach", 5],
index 296afa9500c7063c425754ce3c75fe06a65b92e2..1fd7839e1f0cbd11c13ef052fd6a8eb3fe7ae4b3 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import (QWidget)
 from PySide6.QtGui import (QPainter)
index 28dbacd6f44e09483dc46d1b076efba9146de087..694bb43eb28652d0d161229344e70e582785ccec 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from math import (cos, sin, pi)
 
@@ -11,7 +12,7 @@ from PySide6.QtCore import (QPointF, QSize, Qt)
 PAINTING_SCALE_FACTOR = 20
 
 
-class StarRating(object):
+class StarRating:
     """ Handle the actual painting of the stars themselves. """
 
     def __init__(self, starCount=1, maxStarCount=5):
@@ -40,8 +41,8 @@ class StarRating(object):
         """ Paint the stars (and/or diamonds if we're in editing mode). """
         painter.save()
 
-        painter.setRenderHint(QPainter.Antialiasing, True)
-        painter.setPen(Qt.NoPen)
+        painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
+        painter.setPen(Qt.PenStyle.NoPen)
 
         if isEditable:
             painter.setBrush(palette.highlight())
@@ -54,7 +55,7 @@ class StarRating(object):
 
         for i in range(self.MAX_STAR_COUNT):
             if i < self.star_count:
-                painter.drawPolygon(self._star_polygon, Qt.WindingFill)
+                painter.drawPolygon(self._star_polygon, Qt.FillRule.WindingFill)
             elif isEditable:
                 painter.drawPolygon(self._diamond_polygon, Qt.WindingFill)
             painter.translate(1.0, 0.0)
index 827cb78501e43276933b8a15aa33ed0b9cf5d724..949adc67745335bf8b4fec9fd23dfe0d91ae1629 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/layouts/basiclayout example from Qt v5.x"""
 
@@ -29,7 +30,8 @@ class Dialog(QDialog):
         big_editor.setPlainText("This widget takes up all the remaining space "
                                 "in the top-level layout.")
 
-        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+                                      | QDialogButtonBox.StandardButton.Cancel)
 
         button_box.accepted.connect(self.accept)
         button_box.rejected.connect(self.reject)
index 3268d7034d5c9ef97e370e30eea8a86885078b96..e60c2465f01770682fb47b7bd91effddca90a734 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/layouts/borderlayout example from Qt v5.x"""
 
@@ -62,7 +63,7 @@ class BorderLayout(QLayout):
         self.add(QWidgetItem(widget), position)
 
     def expandingDirections(self) -> Qt.Orientations:
-        return Qt.Horizontal | Qt.Vertical
+        return Qt.Orientation.Horizontal | Qt.Orientation.Vertical
 
     def hasHeightForWidth(self) -> bool:
         return False
@@ -237,7 +238,7 @@ class Window(QWidget):
     @staticmethod
     def create_label(text: str):
         label = QLabel(text)
-        label.setFrameStyle(QFrame.Box | QFrame.Raised)
+        label.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Raised)
         return label
 
 
index c9dfcc73093220da6733ea89cb0bc5cf45080fa3..bff66727fb295e93d91f7ab70cb09b79079761cc 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/layouts/dynamiclayouts example from Qt v5.x"""
 
@@ -25,7 +26,7 @@ class Dialog(QDialog):
         main_layout.addWidget(self._rotable_group_box, 0, 0)
         main_layout.addWidget(self._options_group_box, 1, 0)
         main_layout.addWidget(self._button_box, 2, 0)
-        main_layout.setSizeConstraint(QLayout.SetMinimumSize)
+        main_layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
 
         self._main_layout = main_layout
         self.setLayout(self._main_layout)
@@ -63,7 +64,7 @@ class Dialog(QDialog):
         self._button_box.setOrientation(orientation)
         new_size_hint = self._button_box.sizeHint() + QSize(spacing, spacing)
 
-        if orientation == Qt.Horizontal:
+        if orientation == Qt.Orientation.Horizontal:
             self._main_layout.addWidget(self._button_box, 2, 0)
             self.resize(self.size() + QSize(-old_size_hint.width(), new_size_hint.height()))
         else:
@@ -100,8 +101,8 @@ class Dialog(QDialog):
         buttons_orientation_label = QLabel("Orientation of buttons:")
 
         buttons_orientation_combo_box = QComboBox()
-        buttons_orientation_combo_box.addItem("Horizontal", Qt.Horizontal)
-        buttons_orientation_combo_box.addItem("Vertical", Qt.Vertical)
+        buttons_orientation_combo_box.addItem("Horizontal", Qt.Orientation.Horizontal)
+        buttons_orientation_combo_box.addItem("Vertical", Qt.Orientation.Vertical)
         buttons_orientation_combo_box.currentIndexChanged[int].connect(
             self.buttons_orientation_changed)
 
@@ -116,10 +117,10 @@ class Dialog(QDialog):
     def create_button_box(self):
         self._button_box = QDialogButtonBox()
 
-        close_button = self._button_box.addButton(QDialogButtonBox.Close)
-        help_button = self._button_box.addButton(QDialogButtonBox.Help)
+        close_button = self._button_box.addButton(QDialogButtonBox.StandardButton.Close)
+        help_button = self._button_box.addButton(QDialogButtonBox.StandardButton.Help)
         rotate_widgets_button = self._button_box.addButton(
-            "Rotate &Widgets", QDialogButtonBox.ActionRole)
+            "Rotate &Widgets", QDialogButtonBox.ButtonRole.ActionRole)
 
         rotate_widgets_button.clicked.connect(self.rotate_widgets)
         close_button.clicked.connect(self.close)
index 3ab4318facd8f3ed0a39f230bfa67d63f8797571..856d64204fef6a492f64083d623a57a6c9578d2c 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/layouts/flowlayout example from Qt v6.x"""
 
@@ -90,10 +91,12 @@ class FlowLayout(QLayout):
         for item in self._item_list:
             style = item.widget().style()
             layout_spacing_x = style.layoutSpacing(
-                QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal
+                QSizePolicy.ControlType.PushButton, QSizePolicy.ControlType.PushButton,
+                Qt.Orientation.Horizontal
             )
             layout_spacing_y = style.layoutSpacing(
-                QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical
+                QSizePolicy.ControlType.PushButton, QSizePolicy.ControlType.PushButton,
+                Qt.Orientation.Vertical
             )
             space_x = spacing + layout_spacing_x
             space_y = spacing + layout_spacing_y
index 24a49a617814355c0eee0eb5d52a7a7dedee5a97..169688a4923497c32adc0f0d3cc165ecb5ef6ff7 100644 (file)
@@ -1,5 +1,3 @@
-.. _qt-linguist-example:
-
 Qt Linguist Example
 ===================
 
index e4212e801f60df21c7ec5adc985f7c563f7bdbf3..0feb00638d47c77a137c3b56b2ff5fb07268890d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -24,7 +25,7 @@ class Window(QMainWindow):
         about_qt_action.triggered.connect(qApp.aboutQt)  # noqa: F821
 
         self._list_widget = QListWidget()
-        self._list_widget.setSelectionMode(QAbstractItemView.MultiSelection)
+        self._list_widget.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
         self._list_widget.selectionModel().selectionChanged.connect(self.selection_changed)
         self._list_widget.addItem("C++")
         self._list_widget.addItem("Java")
@@ -41,7 +42,7 @@ class Window(QMainWindow):
 if __name__ == '__main__':
     app = QApplication(sys.argv)
 
-    path = QLibraryInfo.path(QLibraryInfo.TranslationsPath)
+    path = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)
     translator = QTranslator(app)
     if translator.load(QLocale.system(), 'qtbase', '_', path):
         app.installTranslator(translator)
index f69eade2e2486bb2a052490b6cd9ce993cb48708..c3ac90a42b5a1d176b31e65f41b1377d8204ea30 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from argparse import ArgumentParser, RawTextHelpFormatter
 import sys
@@ -83,21 +84,22 @@ class MainWindow(QMainWindow):
 
     def create_actions(self):
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew, QIcon(':/images/new.png'))
-        self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.New,
+        self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.StandardKey.New,
                                 statusTip="Create a new file", triggered=self.new_file)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen, QIcon(':/images/open.png'))
         self._open_act = QAction(icon, "&Open...", self,
-                                 shortcut=QKeySequence.Open, statusTip="Open an existing file",
+                                 shortcut=QKeySequence.StandardKey.Open,
+                                 statusTip="Open an existing file",
                                  triggered=self.open)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave, QIcon(':/images/save.png'))
         self._save_act = QAction(icon, "&Save", self,
-                                 shortcut=QKeySequence.Save,
+                                 shortcut=QKeySequence.StandardKey.Save,
                                  statusTip="Save the document to disk", triggered=self.save)
 
         self._save_as_act = QAction("Save &As...", self,
-                                    shortcut=QKeySequence.SaveAs,
+                                    shortcut=QKeySequence.StandardKey.SaveAs,
                                     statusTip="Save the document under a new name",
                                     triggered=self.save_as)
 
@@ -106,19 +108,19 @@ class MainWindow(QMainWindow):
                                  statusTip="Exit the application", triggered=self.close)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut, QIcon(':/images/cut.png'))
-        self._cut_act = QAction(icon, "Cu&t", self, shortcut=QKeySequence.Cut,
+        self._cut_act = QAction(icon, "Cu&t", self, shortcut=QKeySequence.StandardKey.Cut,
                                 statusTip="Cut the current selection's contents to the clipboard",
                                 triggered=self._text_edit.cut)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy, QIcon(':/images/copy.png'))
         self._copy_act = QAction(icon, "&Copy",
-                                 self, shortcut=QKeySequence.Copy,
+                                 self, shortcut=QKeySequence.StandardKey.Copy,
                                  statusTip="Copy the current selection's contents to the clipboard",
                                  triggered=self._text_edit.copy)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste, QIcon(':/images/paste.png'))
         self._paste_act = QAction(icon, "&Paste",
-                                  self, shortcut=QKeySequence.Paste,
+                                  self, shortcut=QKeySequence.StandardKey.Paste,
                                   statusTip="Paste the clipboard's contents into the current "
                                   "selection",
                                   triggered=self._text_edit.paste)
@@ -186,22 +188,24 @@ class MainWindow(QMainWindow):
             ret = QMessageBox.warning(self, "Application",
                                       "The document has been modified.\nDo you want to save "
                                       "your changes?",
-                                      QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
-            if ret == QMessageBox.Save:
+                                      QMessageBox.StandardButton.Save
+                                      | QMessageBox.StandardButton.Discard
+                                      | QMessageBox.StandardButton.Cancel)
+            if ret == QMessageBox.StandardButton.Save:
                 return self.save()
-            elif ret == QMessageBox.Cancel:
+            elif ret == QMessageBox.StandardButton.Cancel:
                 return False
         return True
 
     def load_file(self, fileName):
         file = QFile(fileName)
-        if not file.open(QFile.ReadOnly | QFile.Text):
+        if not file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
             reason = file.errorString()
             QMessageBox.warning(self, "Application", f"Cannot read file {fileName}:\n{reason}.")
             return
 
         inf = QTextStream(file)
-        with QApplication.setOverrideCursor(Qt.WaitCursor):
+        with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
             self._text_edit.setPlainText(inf.readAll())
 
         self.set_current_file(fileName)
@@ -209,9 +213,9 @@ class MainWindow(QMainWindow):
 
     def save_file(self, fileName):
         error = None
-        with QApplication.setOverrideCursor(Qt.WaitCursor):
+        with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
             file = QSaveFile(fileName)
-            if file.open(QFile.WriteOnly | QFile.Text):
+            if file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
                 outf = QTextStream(file)
                 outf << self._text_edit.toPlainText()
                 if not file.commit():
diff --git a/examples/widgets/mainwindows/application/doc/application.png b/examples/widgets/mainwindows/application/doc/application.png
new file mode 100644 (file)
index 0000000..b5fd459
Binary files /dev/null and b/examples/widgets/mainwindows/application/doc/application.png differ
diff --git a/examples/widgets/mainwindows/application/doc/application.rst b/examples/widgets/mainwindows/application/doc/application.rst
new file mode 100644 (file)
index 0000000..7601370
--- /dev/null
@@ -0,0 +1,12 @@
+Application Example
+===================
+
+This application is a simple text editor built using PySide6, demonstrating
+how to create a modern GUI application with a menu bar, toolbars, and a status
+bar. It supports basic file operations such as creating, opening, saving, and
+editing text files. It demonstrates the analogous Qt example `Application Example
+<https://doc.qt.io/qt-6.2/qtwidgets-mainwindows-application-example.html>`_.
+
+.. image:: application.png
+    :width: 400
+    :alt: application screenshot
index 83487ee58b1727a2fddd38130ee10ab99b9fe993..1f28812b6beaeae872698cc65a8873982cf40133 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/mainwindows/dockwidgets example from Qt v5.x,
    originating from PyQt"""
@@ -39,7 +40,7 @@ class MainWindow(QMainWindow):
         self._text_edit.clear()
 
         cursor = self._text_edit.textCursor()
-        cursor.movePosition(QTextCursor.Start)
+        cursor.movePosition(QTextCursor.MoveOperation.Start)
         top_frame = cursor.currentFrame()
         top_frame_format = top_frame.frameFormat()
         top_frame_format.setPadding(16)
@@ -47,14 +48,14 @@ class MainWindow(QMainWindow):
 
         text_format = QTextCharFormat()
         bold_format = QTextCharFormat()
-        bold_format.setFontWeight(QFont.Bold)
+        bold_format.setFontWeight(QFont.Weight.Bold)
         italic_format = QTextCharFormat()
         italic_format.setFontItalic(True)
 
         table_format = QTextTableFormat()
         table_format.setBorder(1)
         table_format.setCellPadding(16)
-        table_format.setAlignment(Qt.AlignRight)
+        table_format.setAlignment(Qt.AlignmentFlag.AlignRight)
         cursor.insertTable(1, 1, table_format)
         cursor.insertText("The Firm", bold_format)
         cursor.insertBlock()
@@ -84,7 +85,7 @@ class MainWindow(QMainWindow):
         printer = QPrinter()
 
         dlg = QPrintDialog(printer, self)
-        if dlg.exec() != QDialog.Accepted:
+        if dlg.exec() != QDialog.DialogCode.Accepted:
             return
 
         document.print_(printer)
@@ -94,21 +95,21 @@ class MainWindow(QMainWindow):
     def save(self):
         dialog = QFileDialog(self, "Choose a file name")
         dialog.setMimeTypeFilters(['text/html'])
-        dialog.setAcceptMode(QFileDialog.AcceptSave)
+        dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
         dialog.setDefaultSuffix('html')
-        if dialog.exec() != QDialog.Accepted:
+        if dialog.exec() != QDialog.DialogCode.Accepted:
             return
 
         filename = dialog.selectedFiles()[0]
         file = QFile(filename)
-        if not file.open(QFile.WriteOnly | QFile.Text):
+        if not file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
             reason = file.errorString()
             QMessageBox.warning(self, "Dock Widgets",
                                 f"Cannot write file {filename}:\n{reason}.")
             return
 
         out = QTextStream(file)
-        with QApplication.setOverrideCursor(Qt.WaitCursor):
+        with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
             out << self._text_edit.toHtml()
 
         self.statusBar().showMessage(f"Saved '{filename}'", 2000)
@@ -144,8 +145,8 @@ class MainWindow(QMainWindow):
         if cursor.isNull():
             return
         cursor.beginEditBlock()
-        cursor.movePosition(QTextCursor.PreviousBlock,
-                            QTextCursor.MoveAnchor, 2)
+        cursor.movePosition(QTextCursor.MoveOperation.PreviousBlock,
+                            QTextCursor.MoveMode.MoveAnchor, 2)
         cursor.insertBlock()
         cursor.insertText(paragraph)
         cursor.insertBlock()
@@ -161,24 +162,24 @@ class MainWindow(QMainWindow):
     def create_actions(self):
         icon = QIcon.fromTheme('document-new', QIcon(':/images/new.png'))
         self._new_letter_act = QAction(icon, "&New Letter",
-                                       self, shortcut=QKeySequence.New,
+                                       self, shortcut=QKeySequence.StandardKey.New,
                                        statusTip="Create a new form letter",
                                        triggered=self.new_letter)
 
         icon = QIcon.fromTheme('document-save', QIcon(':/images/save.png'))
         self._save_act = QAction(icon, "&Save...", self,
-                                 shortcut=QKeySequence.Save,
+                                 shortcut=QKeySequence.StandardKey.Save,
                                  statusTip="Save the current form letter", triggered=self.save)
 
         icon = QIcon.fromTheme('document-print', QIcon(':/images/print.png'))
         self._print_act = QAction(icon, "&Print...", self,
-                                  shortcut=QKeySequence.Print,
+                                  shortcut=QKeySequence.StandardKey.Print,
                                   statusTip="Print the current form letter",
                                   triggered=self.print_)
 
         icon = QIcon.fromTheme('edit-undo', QIcon(':/images/undo.png'))
         self._undo_act = QAction(icon, "&Undo", self,
-                                 shortcut=QKeySequence.Undo,
+                                 shortcut=QKeySequence.StandardKey.Undo,
                                  statusTip="Undo the last editing action", triggered=self.undo)
 
         self._quit_act = QAction("&Quit", self, shortcut="Ctrl+Q",
@@ -225,7 +226,8 @@ class MainWindow(QMainWindow):
 
     def create_dock_windows(self):
         dock = QDockWidget("Customers", self)
-        dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
+        dock.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea
+                             | Qt.DockWidgetArea.RightDockWidgetArea)
         self._customer_list = QListWidget(dock)
         self._customer_list.addItems((
             "John Doe, Harmony Enterprises, 12 Lakeside, Ambleton",
@@ -235,7 +237,7 @@ class MainWindow(QMainWindow):
             "Sol Harvey, Chicos Coffee, 53 New Springs, Eccleston",
             "Sally Hobart, Tiroli Tea, 67 Long River, Fedula"))
         dock.setWidget(self._customer_list)
-        self.addDockWidget(Qt.RightDockWidgetArea, dock)
+        self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, dock)
         self._view_menu.addAction(dock.toggleViewAction())
 
         dock = QDockWidget("Paragraphs", self)
@@ -259,7 +261,7 @@ class MainWindow(QMainWindow):
             "You made an overpayment (more than $5). Do you wish to buy more "
             "items, or should we return the excess to you?"))
         dock.setWidget(self._paragraphs_list)
-        self.addDockWidget(Qt.RightDockWidgetArea, dock)
+        self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, dock)
         self._view_menu.addAction(dock.toggleViewAction())
 
         self._customer_list.currentTextChanged.connect(self.insert_customer)
diff --git a/examples/widgets/mainwindows/mdi/doc/mdi.png b/examples/widgets/mainwindows/mdi/doc/mdi.png
new file mode 100644 (file)
index 0000000..240f9e2
Binary files /dev/null and b/examples/widgets/mainwindows/mdi/doc/mdi.png differ
diff --git a/examples/widgets/mainwindows/mdi/doc/mdi.rst b/examples/widgets/mainwindows/mdi/doc/mdi.rst
new file mode 100644 (file)
index 0000000..dbbd747
--- /dev/null
@@ -0,0 +1,9 @@
+MDI Example
+===========
+
+The MDI example shows how to implement a Multiple Document Interface using
+Qt's `QMdiArea` class.
+
+.. image:: mdi.png
+    :width: 400
+    :alt: mdi screenshot
index c1fa01b8cf2b8379e74b2d9f0660455d1f7b808f..341a7f4ded75d21da7bd4211d5217872eede7fb3 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/mainwindows/mdi example from Qt v5.x, originating from PyQt"""
 
@@ -23,7 +24,7 @@ class MdiChild(QTextEdit):
     def __init__(self):
         super().__init__()
 
-        self.setAttribute(Qt.WA_DeleteOnClose)
+        self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
         self._is_untitled = True
 
     def new_file(self):
@@ -36,14 +37,14 @@ class MdiChild(QTextEdit):
 
     def load_file(self, fileName):
         file = QFile(fileName)
-        if not file.open(QFile.ReadOnly | QFile.Text):
+        if not file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
             reason = file.errorString()
             message = f"Cannot read file {fileName}:\n{reason}."
             QMessageBox.warning(self, "MDI", message)
             return False
 
         instr = QTextStream(file)
-        with QApplication.setOverrideCursor(Qt.WaitCursor):
+        with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
             self.setPlainText(instr.readAll())
 
         self.set_current_file(fileName)
@@ -67,9 +68,9 @@ class MdiChild(QTextEdit):
 
     def save_file(self, fileName):
         error = None
-        with QApplication.setOverrideCursor(Qt.WaitCursor):
+        with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
             file = QSaveFile(fileName)
-            if file.open(QFile.WriteOnly | QFile.Text):
+            if file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
                 outstr = QTextStream(file)
                 outstr << self.toPlainText()
                 if not file.commit():
@@ -132,8 +133,8 @@ class MainWindow(QMainWindow):
         super().__init__()
 
         self._mdi_area = QMdiArea()
-        self._mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
-        self._mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
+        self._mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
+        self._mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
         self.setCentralWidget(self._mdi_area)
 
         self._mdi_area.subWindowActivated.connect(self.update_menus)
@@ -272,45 +273,43 @@ class MainWindow(QMainWindow):
     def create_actions(self):
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew)
-        self._new_act = QAction(icon, "&New", self,
-                                shortcut=QKeySequence.New, statusTip="Create a new file",
-                                triggered=self.new_file)
+        self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.StandardKey.New,
+                                statusTip="Create a new file", triggered=self.new_file)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen)
-        self._open_act = QAction(icon, "&Open...", self,
-                                 shortcut=QKeySequence.Open, statusTip="Open an existing file",
-                                 triggered=self.open)
+        self._open_act = QAction(icon, "&Open...", self, shortcut=QKeySequence.StandardKey.Open,
+                                 statusTip="Open an existing file", triggered=self.open)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave)
         self._save_act = QAction(icon, "&Save", self,
-                                 shortcut=QKeySequence.Save,
+                                 shortcut=QKeySequence.StandardKey.Save,
                                  statusTip="Save the document to disk", triggered=self.save)
 
         self._save_as_act = QAction("Save &As...", self,
-                                    shortcut=QKeySequence.SaveAs,
+                                    shortcut=QKeySequence.StandardKey.SaveAs,
                                     statusTip="Save the document under a new name",
                                     triggered=self.save_as)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit)
-        self._exit_act = QAction(icon, "E&xit", self, shortcut=QKeySequence.Quit,
+        self._exit_act = QAction(icon, "E&xit", self, shortcut=QKeySequence.StandardKey.Quit,
                                  statusTip="Exit the application",
                                  triggered=QApplication.instance().closeAllWindows)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut)
         self._cut_act = QAction(icon, "Cu&t", self,
-                                shortcut=QKeySequence.Cut,
+                                shortcut=QKeySequence.StandardKey.Cut,
                                 statusTip="Cut the current selection's contents to the clipboard",
                                 triggered=self.cut)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy)
         self._copy_act = QAction(icon, "&Copy", self,
-                                 shortcut=QKeySequence.Copy,
+                                 shortcut=QKeySequence.StandardKey.Copy,
                                  statusTip="Copy the current selection's contents to the clipboard",
                                  triggered=self.copy)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste)
         self._paste_act = QAction(icon, "&Paste", self,
-                                  shortcut=QKeySequence.Paste,
+                                  shortcut=QKeySequence.StandardKey.Paste,
                                   statusTip="Paste the clipboard's contents into the current "
                                             "selection",
                                   triggered=self.paste)
@@ -330,12 +329,12 @@ class MainWindow(QMainWindow):
                                     statusTip="Cascade the windows",
                                     triggered=self._mdi_area.cascadeSubWindows)
 
-        self._next_act = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild,
+        self._next_act = QAction("Ne&xt", self, shortcut=QKeySequence.StandardKey.NextChild,
                                  statusTip="Move the focus to the next window",
                                  triggered=self._mdi_area.activateNextSubWindow)
 
         self._previous_act = QAction("Pre&vious", self,
-                                     shortcut=QKeySequence.PreviousChild,
+                                     shortcut=QKeySequence.StandardKey.PreviousChild,
                                      statusTip="Move the focus to the previous window",
                                      triggered=self._mdi_area.activatePreviousSubWindow)
 
index 858a8cd9fe7f07561c3a39f436cbe246bea0c8a6..ef4af5d855c096865ba9c515f96a83dcf919fd9b 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/painting/basicdrawing example from Qt v5.x, originating from PyQt"""
 
@@ -37,7 +38,7 @@ class RenderArea(QWidget):
         self.transformed = False
         self.pixmap.load(':/images/qt-logo.png')
 
-        self.setBackgroundRole(QPalette.Base)
+        self.setBackgroundRole(QPalette.ColorRole.Base)
         self.setAutoFillBackground(True)
 
     def minimumSizeHint(self):
@@ -81,7 +82,7 @@ class RenderArea(QWidget):
             painter.setPen(self.pen)
             painter.setBrush(self.brush)
             if self.antialiased:
-                painter.setRenderHint(QPainter.Antialiasing)
+                painter.setRenderHint(QPainter.RenderHint.Antialiasing)
 
             for x in range(0, self.width(), 100):
                 for y in range(0, self.height(), 100):
@@ -104,7 +105,7 @@ class RenderArea(QWidget):
                     elif self.shape == RenderArea.Rect:
                         painter.drawRect(rect)
                     elif self.shape == RenderArea.RoundedRect:
-                        painter.drawRoundedRect(rect, 25, 25, Qt.RelativeSize)
+                        painter.drawRoundedRect(rect, 25, 25, Qt.SizeMode.RelativeSize)
                     elif self.shape == RenderArea.Ellipse:
                         painter.drawEllipse(rect)
                     elif self.shape == RenderArea.Arc:
@@ -117,7 +118,7 @@ class RenderArea(QWidget):
                         painter.drawPath(path)
                     elif self.shape == RenderArea.Text:
                         qv = qVersion()
-                        painter.drawText(rect, Qt.AlignCenter,
+                        painter.drawText(rect, Qt.AlignmentFlag.AlignCenter,
                                          f"PySide 6\nQt {qv}")
                     elif self.shape == RenderArea.Pixmap:
                         painter.drawPixmap(10, 10, self.pixmap)
@@ -125,11 +126,11 @@ class RenderArea(QWidget):
                     painter.restore()
 
             painter.setPen(self.palette().dark().color())
-            painter.setBrush(Qt.NoBrush)
+            painter.setBrush(Qt.BrushStyle.NoBrush)
             painter.drawRect(QRect(0, 0, self.width() - 1, self.height() - 1))
 
 
-id_role = Qt.UserRole
+id_role = Qt.ItemDataRole.UserRole
 
 
 class Window(QWidget):
@@ -164,52 +165,53 @@ class Window(QWidget):
         pen_width_label.setBuddy(self._pen_width_spin_box)
 
         self._pen_style_combo_box = QComboBox()
-        self._pen_style_combo_box.addItem("Solid", Qt.SolidLine)
-        self._pen_style_combo_box.addItem("Dash", Qt.DashLine)
-        self._pen_style_combo_box.addItem("Dot", Qt.DotLine)
-        self._pen_style_combo_box.addItem("Dash Dot", Qt.DashDotLine)
-        self._pen_style_combo_box.addItem("Dash Dot Dot", Qt.DashDotDotLine)
-        self._pen_style_combo_box.addItem("None", Qt.NoPen)
+        self._pen_style_combo_box.addItem("Solid", Qt.PenStyle.SolidLine)
+        self._pen_style_combo_box.addItem("Dash", Qt.PenStyle.DashLine)
+        self._pen_style_combo_box.addItem("Dot", Qt.PenStyle.DotLine)
+        self._pen_style_combo_box.addItem("Dash Dot", Qt.PenStyle.DashDotLine)
+        self._pen_style_combo_box.addItem("Dash Dot Dot", Qt.PenStyle.DashDotDotLine)
+        self._pen_style_combo_box.addItem("None", Qt.PenStyle.NoPen)
 
         pen_style_label = QLabel("&Pen Style:")
         pen_style_label.setBuddy(self._pen_style_combo_box)
 
         self._pen_cap_combo_box = QComboBox()
-        self._pen_cap_combo_box.addItem("Flat", Qt.FlatCap)
-        self._pen_cap_combo_box.addItem("Square", Qt.SquareCap)
-        self._pen_cap_combo_box.addItem("Round", Qt.RoundCap)
+        self._pen_cap_combo_box.addItem("Flat", Qt.PenCapStyle.FlatCap)
+        self._pen_cap_combo_box.addItem("Square", Qt.PenCapStyle.SquareCap)
+        self._pen_cap_combo_box.addItem("Round", Qt.PenCapStyle.RoundCap)
 
         pen_cap_label = QLabel("Pen &Cap:")
         pen_cap_label.setBuddy(self._pen_cap_combo_box)
 
         self._pen_join_combo_box = QComboBox()
-        self._pen_join_combo_box.addItem("Miter", Qt.MiterJoin)
-        self._pen_join_combo_box.addItem("Bevel", Qt.BevelJoin)
-        self._pen_join_combo_box.addItem("Round", Qt.RoundJoin)
+        self._pen_join_combo_box.addItem("Miter", Qt.PenJoinStyle.MiterJoin)
+        self._pen_join_combo_box.addItem("Bevel", Qt.PenJoinStyle.BevelJoin)
+        self._pen_join_combo_box.addItem("Round", Qt.PenJoinStyle.RoundJoin)
 
         pen_join_label = QLabel("Pen &Join:")
         pen_join_label.setBuddy(self._pen_join_combo_box)
 
         self._brush_style_combo_box = QComboBox()
-        self._brush_style_combo_box.addItem("Linear Gradient", Qt.LinearGradientPattern)
-        self._brush_style_combo_box.addItem("Radial Gradient", Qt.RadialGradientPattern)
-        self._brush_style_combo_box.addItem("Conical Gradient", Qt.ConicalGradientPattern)
-        self._brush_style_combo_box.addItem("Texture", Qt.TexturePattern)
-        self._brush_style_combo_box.addItem("Solid", Qt.SolidPattern)
-        self._brush_style_combo_box.addItem("Horizontal", Qt.HorPattern)
-        self._brush_style_combo_box.addItem("Vertical", Qt.VerPattern)
-        self._brush_style_combo_box.addItem("Cross", Qt.CrossPattern)
-        self._brush_style_combo_box.addItem("Backward Diagonal", Qt.BDiagPattern)
-        self._brush_style_combo_box.addItem("Forward Diagonal", Qt.FDiagPattern)
-        self._brush_style_combo_box.addItem("Diagonal Cross", Qt.DiagCrossPattern)
-        self._brush_style_combo_box.addItem("Dense 1", Qt.Dense1Pattern)
-        self._brush_style_combo_box.addItem("Dense 2", Qt.Dense2Pattern)
-        self._brush_style_combo_box.addItem("Dense 3", Qt.Dense3Pattern)
-        self._brush_style_combo_box.addItem("Dense 4", Qt.Dense4Pattern)
-        self._brush_style_combo_box.addItem("Dense 5", Qt.Dense5Pattern)
-        self._brush_style_combo_box.addItem("Dense 6", Qt.Dense6Pattern)
-        self._brush_style_combo_box.addItem("Dense 7", Qt.Dense7Pattern)
-        self._brush_style_combo_box.addItem("None", Qt.NoBrush)
+        self._brush_style_combo_box.addItem("Linear Gradient", Qt.BrushStyle.LinearGradientPattern)
+        self._brush_style_combo_box.addItem("Radial Gradient", Qt.BrushStyle.RadialGradientPattern)
+        self._brush_style_combo_box.addItem("Conical Gradient",
+                                            Qt.BrushStyle.ConicalGradientPattern)
+        self._brush_style_combo_box.addItem("Texture", Qt.BrushStyle.TexturePattern)
+        self._brush_style_combo_box.addItem("Solid", Qt.BrushStyle.SolidPattern)
+        self._brush_style_combo_box.addItem("Horizontal", Qt.BrushStyle.HorPattern)
+        self._brush_style_combo_box.addItem("Vertical", Qt.BrushStyle.VerPattern)
+        self._brush_style_combo_box.addItem("Cross", Qt.BrushStyle.CrossPattern)
+        self._brush_style_combo_box.addItem("Backward Diagonal", Qt.BrushStyle.BDiagPattern)
+        self._brush_style_combo_box.addItem("Forward Diagonal", Qt.BrushStyle.FDiagPattern)
+        self._brush_style_combo_box.addItem("Diagonal Cross", Qt.BrushStyle.DiagCrossPattern)
+        self._brush_style_combo_box.addItem("Dense 1", Qt.BrushStyle.Dense1Pattern)
+        self._brush_style_combo_box.addItem("Dense 2", Qt.BrushStyle.Dense2Pattern)
+        self._brush_style_combo_box.addItem("Dense 3", Qt.BrushStyle.Dense3Pattern)
+        self._brush_style_combo_box.addItem("Dense 4", Qt.BrushStyle.Dense4Pattern)
+        self._brush_style_combo_box.addItem("Dense 5", Qt.BrushStyle.Dense5Pattern)
+        self._brush_style_combo_box.addItem("Dense 6", Qt.BrushStyle.Dense6Pattern)
+        self._brush_style_combo_box.addItem("Dense 7", Qt.BrushStyle.Dense7Pattern)
+        self._brush_style_combo_box.addItem("None", Qt.BrushStyle.NoBrush)
 
         brush_style_label = QLabel("&Brush Style:")
         brush_style_label.setBuddy(self._brush_style_combo_box)
@@ -232,20 +234,20 @@ class Window(QWidget):
         main_layout.setColumnStretch(3, 1)
         main_layout.addWidget(self._render_area, 0, 0, 1, 4)
         main_layout.setRowMinimumHeight(1, 6)
-        main_layout.addWidget(shape_label, 2, 1, Qt.AlignRight)
+        main_layout.addWidget(shape_label, 2, 1, Qt.AlignmentFlag.AlignRight)
         main_layout.addWidget(self._shape_combo_box, 2, 2)
-        main_layout.addWidget(pen_width_label, 3, 1, Qt.AlignRight)
+        main_layout.addWidget(pen_width_label, 3, 1, Qt.AlignmentFlag.AlignRight)
         main_layout.addWidget(self._pen_width_spin_box, 3, 2)
-        main_layout.addWidget(pen_style_label, 4, 1, Qt.AlignRight)
+        main_layout.addWidget(pen_style_label, 4, 1, Qt.AlignmentFlag.AlignRight)
         main_layout.addWidget(self._pen_style_combo_box, 4, 2)
-        main_layout.addWidget(pen_cap_label, 5, 1, Qt.AlignRight)
+        main_layout.addWidget(pen_cap_label, 5, 1, Qt.AlignmentFlag.AlignRight)
         main_layout.addWidget(self._pen_cap_combo_box, 5, 2)
-        main_layout.addWidget(pen_join_label, 6, 1, Qt.AlignRight)
+        main_layout.addWidget(pen_join_label, 6, 1, Qt.AlignmentFlag.AlignRight)
         main_layout.addWidget(self._pen_join_combo_box, 6, 2)
-        main_layout.addWidget(brush_style_label, 7, 1, Qt.AlignRight)
+        main_layout.addWidget(brush_style_label, 7, 1, Qt.AlignmentFlag.AlignRight)
         main_layout.addWidget(self._brush_style_combo_box, 7, 2)
         main_layout.setRowMinimumHeight(8, 6)
-        main_layout.addWidget(other_options_label, 9, 1, Qt.AlignRight)
+        main_layout.addWidget(other_options_label, 9, 1, Qt.AlignmentFlag.AlignRight)
         main_layout.addWidget(self._antialiasing_check_box, 9, 2)
         main_layout.addWidget(self._transformations_check_box, 10, 2)
         self.setLayout(main_layout)
@@ -270,34 +272,34 @@ class Window(QWidget):
         join = Qt.PenJoinStyle(self._pen_join_combo_box.itemData(
             self._pen_join_combo_box.currentIndex(), id_role))
 
-        self._render_area.set_pen(QPen(Qt.blue, width, style, cap, join))
+        self._render_area.set_pen(QPen(Qt.GlobalColor.blue, width, style, cap, join))
 
     def brush_changed(self):
         style = Qt.BrushStyle(self._brush_style_combo_box.itemData(
             self._brush_style_combo_box.currentIndex(), id_role))
 
-        if style == Qt.LinearGradientPattern:
+        if style == Qt.BrushStyle.LinearGradientPattern:
             linear_gradient = QLinearGradient(0, 0, 100, 100)
-            linear_gradient.setColorAt(0.0, Qt.white)
-            linear_gradient.setColorAt(0.2, Qt.green)
-            linear_gradient.setColorAt(1.0, Qt.black)
+            linear_gradient.setColorAt(0.0, Qt.GlobalColor.white)
+            linear_gradient.setColorAt(0.2, Qt.GlobalColor.green)
+            linear_gradient.setColorAt(1.0, Qt.GlobalColor.black)
             self._render_area.set_brush(QBrush(linear_gradient))
-        elif style == Qt.RadialGradientPattern:
+        elif style == Qt.BrushStyle.RadialGradientPattern:
             radial_gradient = QRadialGradient(50, 50, 50, 70, 70)
-            radial_gradient.setColorAt(0.0, Qt.white)
-            radial_gradient.setColorAt(0.2, Qt.green)
-            radial_gradient.setColorAt(1.0, Qt.black)
+            radial_gradient.setColorAt(0.0, Qt.GlobalColor.white)
+            radial_gradient.setColorAt(0.2, Qt.GlobalColor.green)
+            radial_gradient.setColorAt(1.0, Qt.GlobalColor.black)
             self._render_area.set_brush(QBrush(radial_gradient))
-        elif style == Qt.ConicalGradientPattern:
+        elif style == Qt.BrushStyle.ConicalGradientPattern:
             conical_gradient = QConicalGradient(50, 50, 150)
-            conical_gradient.setColorAt(0.0, Qt.white)
-            conical_gradient.setColorAt(0.2, Qt.green)
-            conical_gradient.setColorAt(1.0, Qt.black)
+            conical_gradient.setColorAt(0.0, Qt.GlobalColor.white)
+            conical_gradient.setColorAt(0.2, Qt.GlobalColor.green)
+            conical_gradient.setColorAt(1.0, Qt.GlobalColor.black)
             self._render_area.set_brush(QBrush(conical_gradient))
-        elif style == Qt.TexturePattern:
+        elif style == Qt.BrushStyle.TexturePattern:
             self._render_area.set_brush(QBrush(QPixmap(':/images/brick.png')))
         else:
-            self._render_area.set_brush(QBrush(Qt.green, style))
+            self._render_area.set_brush(QBrush(Qt.GlobalColor.green, style))
 
 
 if __name__ == '__main__':
index d2c60178f103cc4b3fe1afec090ae5d65843771a..4eef4df7763fb383ae533e58332a9802308ef903 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/painting/concentriccircles example from Qt v5.x, originating
    from PyQt"""
@@ -19,8 +20,8 @@ class CircleWidget(QWidget):
         self.antialiased = False
         self._frame_no = 0
 
-        self.setBackgroundRole(QPalette.Base)
-        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+        self.setBackgroundRole(QPalette.ColorRole.Base)
+        self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
 
     def set_float_based(self, floatBased):
         self._float_based = floatBased
@@ -42,7 +43,7 @@ class CircleWidget(QWidget):
 
     def paintEvent(self, event):
         with QPainter(self) as painter:
-            painter.setRenderHint(QPainter.Antialiasing, self.antialiased)
+            painter.setRenderHint(QPainter.RenderHint.Antialiasing, self.antialiased)
             painter.translate(self.width() / 2, self.height() / 2)
 
             for diameter in range(0, 256, 9):
@@ -93,9 +94,9 @@ class Window(QWidget):
 
     def create_label(self, text):
         label = QLabel(text)
-        label.setAlignment(Qt.AlignCenter)
+        label.setAlignment(Qt.AlignmentFlag.AlignCenter)
         label.setMargin(2)
-        label.setFrameStyle(QFrame.Box | QFrame.Sunken)
+        label.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Sunken)
         return label
 
 
index 2ca078ad913fb6a148eb676ff05fed0c9d744a3e..4b009b30dc309ec569035c8416bab5b06788d029 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import (
     QWidget,
@@ -36,14 +37,14 @@ class PainterWidget(QWidget):
 
         self.setFixedSize(680, 480)
         self.pixmap = QPixmap(self.size())
-        self.pixmap.fill(Qt.white)
+        self.pixmap.fill(Qt.GlobalColor.white)
 
         self.previous_pos = None
         self.painter = QPainter()
         self.pen = QPen()
         self.pen.setWidth(10)
-        self.pen.setCapStyle(Qt.RoundCap)
-        self.pen.setJoinStyle(Qt.RoundJoin)
+        self.pen.setCapStyle(Qt.PenCapStyle.RoundCap)
+        self.pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
 
     def paintEvent(self, event: QPaintEvent):
         """Override method from QWidget
@@ -71,7 +72,7 @@ class PainterWidget(QWidget):
         """
         current_pos = event.position().toPoint()
         self.painter.begin(self.pixmap)
-        self.painter.setRenderHints(QPainter.Antialiasing, True)
+        self.painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
         self.painter.setPen(self.pen)
         self.painter.drawLine(self.previous_pos, current_pos)
         self.painter.end()
@@ -97,12 +98,12 @@ class PainterWidget(QWidget):
     def load(self, filename: str):
         """ load pixmap from filename """
         self.pixmap.load(filename)
-        self.pixmap = self.pixmap.scaled(self.size(), Qt.KeepAspectRatio)
+        self.pixmap = self.pixmap.scaled(self.size(), Qt.AspectRatioMode.KeepAspectRatio)
         self.update()
 
     def clear(self):
         """ Clear the pixmap """
-        self.pixmap.fill(Qt.white)
+        self.pixmap.fill(Qt.GlobalColor.white)
         self.update()
 
 
@@ -114,19 +115,19 @@ class MainWindow(QMainWindow):
 
         self.painter_widget = PainterWidget()
         self.bar = self.addToolBar("Menu")
-        self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
+        self.bar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
         self._save_action = self.bar.addAction(
-            qApp.style().standardIcon(QStyle.SP_DialogSaveButton),  # noqa: F821
+            qApp.style().standardIcon(QStyle.StandardPixmap.SP_DialogSaveButton),  # noqa: F821
             "Save", self.on_save
         )
-        self._save_action.setShortcut(QKeySequence.Save)
+        self._save_action.setShortcut(QKeySequence.StandardKey.Save)
         self._open_action = self.bar.addAction(
-            qApp.style().standardIcon(QStyle.SP_DialogOpenButton),  # noqa: F821
+            qApp.style().standardIcon(QStyle.StandardPixmap.SP_DialogOpenButton),  # noqa: F821
             "Open", self.on_open
         )
-        self._open_action.setShortcut(QKeySequence.Open)
+        self._open_action.setShortcut(QKeySequence.StandardKey.Open)
         self.bar.addAction(
-            qApp.style().standardIcon(QStyle.SP_DialogResetButton),  # noqa: F821
+            qApp.style().standardIcon(QStyle.StandardPixmap.SP_DialogResetButton),  # noqa: F821
             "Clear",
             self.painter_widget.clear,
         )
@@ -138,7 +139,7 @@ class MainWindow(QMainWindow):
 
         self.setCentralWidget(self.painter_widget)
 
-        self.color = Qt.black
+        self.color = Qt.GlobalColor.black
         self.set_color(self.color)
 
         self.mime_type_filters = ["image/png", "image/jpeg"]
@@ -148,14 +149,14 @@ class MainWindow(QMainWindow):
 
         dialog = QFileDialog(self, "Save File")
         dialog.setMimeTypeFilters(self.mime_type_filters)
-        dialog.setFileMode(QFileDialog.AnyFile)
-        dialog.setAcceptMode(QFileDialog.AcceptSave)
+        dialog.setFileMode(QFileDialog.FileMode.AnyFile)
+        dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
         dialog.setDefaultSuffix("png")
         dialog.setDirectory(
-            QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
+            QStandardPaths.writableLocation(QStandardPaths.StandardLocation.PicturesLocation)
         )
 
-        if dialog.exec() == QFileDialog.Accepted:
+        if dialog.exec() == QFileDialog.DialogCode.Accepted:
             if dialog.selectedFiles():
                 self.painter_widget.save(dialog.selectedFiles()[0])
 
@@ -164,14 +165,14 @@ class MainWindow(QMainWindow):
 
         dialog = QFileDialog(self, "Save File")
         dialog.setMimeTypeFilters(self.mime_type_filters)
-        dialog.setFileMode(QFileDialog.ExistingFile)
-        dialog.setAcceptMode(QFileDialog.AcceptOpen)
+        dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
+        dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
         dialog.setDefaultSuffix("png")
         dialog.setDirectory(
-            QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
+            QStandardPaths.writableLocation(QStandardPaths.StandardLocation.PicturesLocation)
         )
 
-        if dialog.exec() == QFileDialog.Accepted:
+        if dialog.exec() == QFileDialog.DialogCode.Accepted:
             if dialog.selectedFiles():
                 self.painter_widget.load(dialog.selectedFiles()[0])
 
@@ -183,7 +184,7 @@ class MainWindow(QMainWindow):
         if color:
             self.set_color(color)
 
-    def set_color(self, color: QColor = Qt.black):
+    def set_color(self, color: QColor = Qt.GlobalColor.black):
 
         self.color = color
         # Create color icon
index fd7ff993798d30b1f75970017a57256eea525710..5e564511f572136d5e58b435797e5ebe6b76ff5c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import math
 import sys
@@ -52,7 +53,7 @@ class PlotWidget(QWidget):
     def paintEvent(self, event):
         with QPainter(self) as painter:
             rect = QRect(QPoint(0, 0), self.size())
-            painter.fillRect(rect, Qt.white)
+            painter.fillRect(rect, Qt.GlobalColor.white)
             painter.translate(-self._points[0].x(), 0)
             painter.drawPolyline(self._points)
 
index c33c6660a1b7ce7eb9d4a413fc9af066fad50ebc..65cfacd85d84f4f19f4c1b2ba45ae89ece77ec21 100644 (file)
@@ -1,12 +1,10 @@
-.. _rhi-widget-example:
-
 Simple RHI Widget Example
 =========================
 
 Shows how to render a triangle using ``QRhi``, Qt's 3D API and shading
 language abstraction layer.
 
-This example is, in many ways, the counterpart of the :ref:`rhi-window-example`
+This example is, in many ways, the counterpart of the :ref:`example_gui_rhiwindow`
 in the QWidget world. The ``QRhiWidget`` subclass in this applications renders
 a single triangle, using a simple graphics pipeline with basic vertex and
 fragment shaders. Unlike the plain ``QWindow``-based application, this example
index 5b3e40f508e3bc2353721fcd01d9ac68b9d31870..732c8e5556967c3fadd5a5baff0e3439f41b0685 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import numpy
 
@@ -23,7 +24,7 @@ VERTEX_DATA = numpy.array([ 0.0,  0.5, 1.0, 0.0, 0.0,  # noqa E:201
 
 def getShader(name):
     f = QFile(name)
-    if f.open(QIODevice.ReadOnly):
+    if f.open(QIODevice.OpenModeFlag.ReadOnly):
         return QShader.fromSerialized(f.readAll())
     return QShader()
 
@@ -61,17 +62,17 @@ class ExampleRhiWidget(QRhiWidget):
 
         if not self.m_pipeline:
             vertex_size = 4 * VERTEX_DATA.size
-            self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Immutable,
-                                               QRhiBuffer.VertexBuffer, vertex_size)
+            self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Immutable,
+                                               QRhiBuffer.UsageFlag.VertexBuffer, vertex_size)
             self.m_vbuf.create()
 
-            self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Dynamic,
-                                               QRhiBuffer.UniformBuffer, 64)
+            self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Dynamic,
+                                               QRhiBuffer.UsageFlag.UniformBuffer, 64)
             self.m_ubuf.create()
 
             self.m_srb = self.m_rhi.newShaderResourceBindings()
             bindings = [
-                QRhiShaderResourceBinding.uniformBuffer(0, QRhiShaderResourceBinding.VertexStage,
+                QRhiShaderResourceBinding.uniformBuffer(0, QRhiShaderResourceBinding.StageFlag.VertexStage,  # noqa: E501
                                                         self.m_ubuf)
             ]
             self.m_srb.setBindings(bindings)
@@ -79,9 +80,9 @@ class ExampleRhiWidget(QRhiWidget):
 
             self.m_pipeline = self.m_rhi.newGraphicsPipeline()
             stages = [
-                QRhiShaderStage(QRhiShaderStage.Vertex,
+                QRhiShaderStage(QRhiShaderStage.Type.Vertex,
                                 getShader(":/shader_assets/color.vert.qsb")),
-                QRhiShaderStage(QRhiShaderStage.Fragment,
+                QRhiShaderStage(QRhiShaderStage.Type.Fragment,
                                 getShader(":/shader_assets/color.frag.qsb"))
             ]
             self.m_pipeline.setShaderStages(stages)
@@ -89,8 +90,8 @@ class ExampleRhiWidget(QRhiWidget):
             input_bindings = [QRhiVertexInputBinding(5 * 4)]  # sizeof(float)
             inputLayout.setBindings(input_bindings)
             attributes = [  # 4: sizeof(float)
-                QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Float2, 0),
-                QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Float3, 2 * 4)
+                QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Format.Float2, 0),
+                QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Format.Float3, 2 * 4)
             ]
             inputLayout.setAttributes(attributes)
             self.m_pipeline.setVertexInputLayout(inputLayout)
index 59be81ddcaf9ff7409b563cba72cf7829bd1ef63..5b23af0df612829cbbdf81153d2f81dcaa844571 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the Qt Simple RHI Widget Example example from Qt v6.x"""
 
diff --git a/examples/widgets/richtext/orderform/doc/orderform.rst b/examples/widgets/richtext/orderform/doc/orderform.rst
new file mode 100644 (file)
index 0000000..ce5e53f
--- /dev/null
@@ -0,0 +1,9 @@
+Order Form Example
+==================
+
+The Order Form example shows how to generate rich text documents by combining a
+simple template with data input by the user in a dialog
+
+.. image:: orderform.webp
+    :width: 400
+    :alt: orderform screenshot
diff --git a/examples/widgets/richtext/orderform/doc/orderform.webp b/examples/widgets/richtext/orderform/doc/orderform.webp
new file mode 100644 (file)
index 0000000..501ea16
Binary files /dev/null and b/examples/widgets/richtext/orderform/doc/orderform.webp differ
index 9725624c399c0406ffd845fbdf18388c0b03f956..6e0818e1cc6feeaa05e0bd0d397cf8fa892ec526 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/richtext/orderform example from Qt v5.x"""
 
@@ -45,7 +46,7 @@ class MainWindow(QMainWindow):
         self.letters.setCurrentIndex(tab_index)
 
         cursor = editor.textCursor()
-        cursor.movePosition(QTextCursor.Start)
+        cursor.movePosition(QTextCursor.MoveOperation.Start)
         top_frame = cursor.currentFrame()
         top_frame_format = top_frame.frameFormat()
         top_frame_format.setPadding(16)
@@ -53,13 +54,13 @@ class MainWindow(QMainWindow):
 
         text_format = QTextCharFormat()
         bold_format = QTextCharFormat()
-        bold_format.setFontWeight(QFont.Bold)
+        bold_format.setFontWeight(QFont.Weight.Bold)
 
         reference_frame_format = QTextFrameFormat()
         reference_frame_format.setBorder(1)
         reference_frame_format.setPadding(8)
-        reference_frame_format.setPosition(QTextFrameFormat.FloatRight)
-        reference_frame_format.setWidth(QTextLength(QTextLength.PercentageLength, 40))
+        reference_frame_format.setPosition(QTextFrameFormat.Position.FloatRight)
+        reference_frame_format.setWidth(QTextLength(QTextLength.Type.PercentageLength, 40))
         cursor.insertFrame(reference_frame_format)
 
         cursor.insertText("A company", bold_format)
@@ -86,7 +87,7 @@ class MainWindow(QMainWindow):
         cursor.insertBlock()
 
         body_frame_format = QTextFrameFormat()
-        body_frame_format.setWidth(QTextLength(QTextLength.PercentageLength, 100))
+        body_frame_format.setWidth(QTextLength(QTextLength.Type.PercentageLength, 100))
         cursor.insertFrame(body_frame_format)
 
         cursor.insertText("I would like to place an order for the following items:", text_format)
@@ -94,7 +95,7 @@ class MainWindow(QMainWindow):
         cursor.insertBlock()
 
         order_table_format = QTextTableFormat()
-        order_table_format.setAlignment(Qt.AlignHCenter)
+        order_table_format.setAlignment(Qt.AlignmentFlag.AlignHCenter)
         order_table = cursor.insertTable(1, 2, order_table_format)
 
         order_frame_format = cursor.currentFrame().frameFormat()
@@ -188,7 +189,7 @@ class DetailsDialog(QDialog):
 
         name_label = QLabel("Name:")
         address_label = QLabel("Address:")
-        address_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
+        address_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
 
         self._name_edit = QLineEdit()
         self._address_edit = QTextEdit()
@@ -196,7 +197,8 @@ class DetailsDialog(QDialog):
 
         self.setup_items_table()
 
-        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+                                      | QDialogButtonBox.StandardButton.Cancel)
 
         button_box.accepted.connect(self.verify)
         button_box.rejected.connect(self.reject)
@@ -217,7 +219,7 @@ class DetailsDialog(QDialog):
 
         for row, item in enumerate(self.items):
             name = QTableWidgetItem(item)
-            name.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
+            name.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable)
             self._items_table.setItem(row, 0, name)
             quantity = QTableWidgetItem('1')
             self._items_table.setItem(row, 1, quantity)
@@ -227,7 +229,7 @@ class DetailsDialog(QDialog):
 
         for row in range(len(self.items)):
             text = self._items_table.item(row, 0).text()
-            quantity = int(self._items_table.item(row, 1).data(Qt.DisplayRole))
+            quantity = int(self._items_table.item(row, 1).data(Qt.ItemDataRole.DisplayRole))
             order_list.append((text, max(0, quantity)))
 
         return order_list
index 021328977ff725ab70c13504eedbc6313dc50283..46534b1893e20f7b3f1d2b0d6c48df28a86bffa8 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/richtext/syntaxhighlighter example from Qt v5.x"""
 
@@ -39,20 +40,20 @@ class MainWindow(QMainWindow):
 
         if file_name:
             in_file = QFile(file_name)
-            if in_file.open(QFile.ReadOnly | QFile.Text):
+            if in_file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
                 stream = QTextStream(in_file)
                 self._editor.setPlainText(stream.readAll())
 
     def setup_editor(self):
         class_format = QTextCharFormat()
-        class_format.setFontWeight(QFont.Bold)
-        class_format.setForeground(Qt.blue)
+        class_format.setFontWeight(QFont.Weight.Bold)
+        class_format.setForeground(Qt.GlobalColor.blue)
         pattern = r'^\s*class\s+\w+\(.*$'
         self._highlighter.add_mapping(pattern, class_format)
 
         function_format = QTextCharFormat()
         function_format.setFontItalic(True)
-        function_format.setForeground(Qt.blue)
+        function_format.setForeground(Qt.GlobalColor.blue)
         pattern = r'^\s*def\s+\w+\s*\(.*\)\s*:\s*$'
         self._highlighter.add_mapping(pattern, function_format)
 
@@ -60,7 +61,7 @@ class MainWindow(QMainWindow):
         comment_format.setBackground(QColor("#77ff77"))
         self._highlighter.add_mapping(r'^\s*#.*$', comment_format)
 
-        font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
+        font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
         self._editor = QPlainTextEdit()
         self._editor.setFont(font)
         self._highlighter.setDocument(self._editor.document())
@@ -69,15 +70,15 @@ class MainWindow(QMainWindow):
         file_menu = self.menuBar().addMenu(self.tr("&File"))
 
         new_file_act = file_menu.addAction(self.tr("&New..."))
-        new_file_act.setShortcut(QKeySequence(QKeySequence.New))
+        new_file_act.setShortcut(QKeySequence(QKeySequence.StandardKey.New))
         new_file_act.triggered.connect(self.new_file)
 
         open_file_act = file_menu.addAction(self.tr("&Open..."))
-        open_file_act.setShortcut(QKeySequence(QKeySequence.Open))
+        open_file_act.setShortcut(QKeySequence(QKeySequence.StandardKey.Open))
         open_file_act.triggered.connect(self.open_file)
 
         quit_act = file_menu.addAction(self.tr("E&xit"))
-        quit_act.setShortcut(QKeySequence(QKeySequence.Quit))
+        quit_act.setShortcut(QKeySequence(QKeySequence.StandardKey.Quit))
         quit_act.triggered.connect(self.close)
 
         help_menu = self.menuBar().addMenu("&Help")
index 330ea5fc5863186d3f4e3520e5764c5ff08fcd6d..3bebace780de5081c981f6f20902c709bd77b3bf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/richtext/textedit example from Qt v6.x"""
 
index 8019446f0a3f6180dcc22e8e2448e5d435841b3a..8cd330a72354156f9b0e2d2745000f88231cab5e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import (QCoreApplication, QDir, QFile, QFileInfo,
@@ -48,7 +49,7 @@ class TextEdit(QMainWindow):
         self._text_edit.cursorPositionChanged.connect(self.cursor_position_changed)
         self.setCentralWidget(self._text_edit)
 
-        self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
+        self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonFollowStyle)
         self.setup_file_actions()
         self.setup_edit_actions()
         self.setup_text_actions()
@@ -58,7 +59,7 @@ class TextEdit(QMainWindow):
         help_menu.addAction("About &Qt", qApp.aboutQt)  # noqa: F821
 
         text_font = QFont("Helvetica")
-        text_font.setStyleHint(QFont.SansSerif)
+        text_font.setStyleHint(QFont.StyleHint.SansSerif)
         self._text_edit.setFont(text_font)
         self.font_changed(self._text_edit.font())
         self.color_changed(self._text_edit.textColor())
@@ -87,8 +88,8 @@ class TextEdit(QMainWindow):
         # Use dark text on light background on macOS, also in dark mode.
         if sys.platform == 'darwin':
             pal = self._text_edit.palette()
-            pal.setColor(QPalette.Base, QColor(Qt.white))
-            pal.setColor(QPalette.Text, QColor(Qt.black))
+            pal.setColor(QPalette.Base, QColor(Qt.GlobalColor.white))
+            pal.setColor(QPalette.Text, QColor(Qt.GlobalColor.black))
             self._text_edit.setPalette(pal)
 
     def closeEvent(self, e):
@@ -105,13 +106,13 @@ class TextEdit(QMainWindow):
                                QIcon(RSRC_PATH + "/filenew.png"))
         a = menu.addAction(icon, "&New", self.file_new)
         tb.addAction(a)
-        a.setPriority(QAction.LowPriority)
-        a.setShortcut(QKeySequence.New)
+        a.setPriority(QAction.Priority.LowPriority)
+        a.setShortcut(QKeySequence.StandardKey.New)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen,
                                QIcon(RSRC_PATH + "/fileopen.png"))
         a = menu.addAction(icon, "&Open...", self.file_open)
-        a.setShortcut(QKeySequence.Open)
+        a.setShortcut(QKeySequence.StandardKey.Open)
         tb.addAction(a)
 
         menu.addSeparator()
@@ -119,19 +120,19 @@ class TextEdit(QMainWindow):
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave,
                                QIcon(RSRC_PATH + "/filesave.png"))
         self._action_save = menu.addAction(icon, "&Save", self.file_save)
-        self._action_save.setShortcut(QKeySequence.Save)
+        self._action_save.setShortcut(QKeySequence.StandardKey.Save)
         self._action_save.setEnabled(False)
         tb.addAction(self._action_save)
 
         a = menu.addAction("Save &As...", self.file_save_as)
-        a.setPriority(QAction.LowPriority)
+        a.setPriority(QAction.Priority.LowPriority)
         menu.addSeparator()
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentPrint,
                                QIcon(RSRC_PATH + "/fileprint.png"))
         a = menu.addAction(icon, "&Print...", self.file_print)
-        a.setPriority(QAction.LowPriority)
-        a.setShortcut(QKeySequence.Print)
+        a.setPriority(QAction.Priority.LowPriority)
+        a.setShortcut(QKeySequence.StandardKey.Print)
         tb.addAction(a)
 
         icon = QIcon.fromTheme("fileprint", QIcon(RSRC_PATH + "/fileprint.png"))
@@ -139,14 +140,14 @@ class TextEdit(QMainWindow):
 
         icon = QIcon.fromTheme("exportpdf", QIcon(RSRC_PATH + "/exportpdf.png"))
         a = menu.addAction(icon, "&Export PDF...", self.file_print_pdf)
-        a.setPriority(QAction.LowPriority)
-        a.setShortcut(Qt.CTRL | Qt.Key_D)
+        a.setPriority(QAction.Priority.LowPriority)
+        a.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_D)
         tb.addAction(a)
 
         menu.addSeparator()
 
         a = menu.addAction("&Quit", self.close)
-        a.setShortcut(Qt.CTRL | Qt.Key_Q)
+        a.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Q)
 
     def setup_edit_actions(self):
         tb = self.addToolBar("Edit self.actions")
@@ -155,36 +156,36 @@ class TextEdit(QMainWindow):
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditUndo,
                                QIcon(RSRC_PATH + "/editundo.png"))
         self._action_undo = menu.addAction(icon, "&Undo", self._text_edit.undo)
-        self._action_undo.setShortcut(QKeySequence.Undo)
+        self._action_undo.setShortcut(QKeySequence.StandardKey.Undo)
         tb.addAction(self._action_undo)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditRedo,
                                QIcon(RSRC_PATH + "/editredo.png"))
         self._action_redo = menu.addAction(icon, "&Redo", self._text_edit.redo)
-        self._action_redo.setPriority(QAction.LowPriority)
-        self._action_redo.setShortcut(QKeySequence.Redo)
+        self._action_redo.setPriority(QAction.Priority.LowPriority)
+        self._action_redo.setShortcut(QKeySequence.StandardKey.Redo)
         tb.addAction(self._action_redo)
         menu.addSeparator()
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut,
                                QIcon(RSRC_PATH + "/editcut.png"))
         self._action_cut = menu.addAction(icon, "Cu&t", self._text_edit.cut)
-        self._action_cut.setPriority(QAction.LowPriority)
-        self._action_cut.setShortcut(QKeySequence.Cut)
+        self._action_cut.setPriority(QAction.Priority.LowPriority)
+        self._action_cut.setShortcut(QKeySequence.StandardKey.Cut)
         tb.addAction(self._action_cut)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy,
                                QIcon(RSRC_PATH + "/editcopy.png"))
         self._action_copy = menu.addAction(icon, "&Copy", self._text_edit.copy)
-        self._action_copy.setPriority(QAction.LowPriority)
-        self._action_copy.setShortcut(QKeySequence.Copy)
+        self._action_copy.setPriority(QAction.Priority.LowPriority)
+        self._action_copy.setShortcut(QKeySequence.StandardKey.Copy)
         tb.addAction(self._action_copy)
 
         icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste,
                                QIcon(RSRC_PATH + "/editpaste.png"))
         self._action_paste = menu.addAction(icon, "&Paste", self._text_edit.paste)
-        self._action_paste.setPriority(QAction.LowPriority)
-        self._action_paste.setShortcut(QKeySequence.Paste)
+        self._action_paste.setPriority(QAction.Priority.LowPriority)
+        self._action_paste.setShortcut(QKeySequence.StandardKey.Paste)
         tb.addAction(self._action_paste)
 
         md = QGuiApplication.clipboard().mimeData()
@@ -198,8 +199,8 @@ class TextEdit(QMainWindow):
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextBold,
                                QIcon(RSRC_PATH + "/textbold.png"))
         self._action_text_bold = menu.addAction(icon, "&Bold", self.text_bold)
-        self._action_text_bold.setShortcut(Qt.CTRL | Qt.Key_B)
-        self._action_text_bold.setPriority(QAction.LowPriority)
+        self._action_text_bold.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_B)
+        self._action_text_bold.setPriority(QAction.Priority.LowPriority)
         bold = QFont()
         bold.setBold(True)
         self._action_text_bold.setFont(bold)
@@ -209,8 +210,8 @@ class TextEdit(QMainWindow):
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextItalic,
                                QIcon(RSRC_PATH + "/textitalic.png"))
         self._action_text_italic = menu.addAction(icon, "&Italic", self.text_italic)
-        self._action_text_italic.setPriority(QAction.LowPriority)
-        self._action_text_italic.setShortcut(Qt.CTRL | Qt.Key_I)
+        self._action_text_italic.setPriority(QAction.Priority.LowPriority)
+        self._action_text_italic.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_I)
         italic = QFont()
         italic.setItalic(True)
         self._action_text_italic.setFont(italic)
@@ -221,8 +222,8 @@ class TextEdit(QMainWindow):
                                QIcon(RSRC_PATH + "/textunder.png"))
         self._action_text_underline = menu.addAction(icon, "&Underline",
                                                      self.text_underline)
-        self._action_text_underline.setShortcut(Qt.CTRL | Qt.Key_U)
-        self._action_text_underline.setPriority(QAction.LowPriority)
+        self._action_text_underline.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_U)
+        self._action_text_underline.setPriority(QAction.Priority.LowPriority)
         underline = QFont()
         underline.setUnderline(True)
         self._action_text_underline.setFont(underline)
@@ -234,38 +235,38 @@ class TextEdit(QMainWindow):
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyLeft,
                                QIcon(RSRC_PATH + "/textleft.png"))
         self._action_align_left = QAction(icon, "&Left", self)
-        self._action_align_left.setShortcut(Qt.CTRL | Qt.Key_L)
+        self._action_align_left.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_L)
         self._action_align_left.setCheckable(True)
-        self._action_align_left.setPriority(QAction.LowPriority)
+        self._action_align_left.setPriority(QAction.Priority.LowPriority)
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyCenter,
                                QIcon(RSRC_PATH + "/textcenter.png"))
         self._action_align_center = QAction(icon, "C&enter", self)
-        self._action_align_center.setShortcut(Qt.CTRL | Qt.Key_E)
+        self._action_align_center.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_E)
         self._action_align_center.setCheckable(True)
-        self._action_align_center.setPriority(QAction.LowPriority)
+        self._action_align_center.setPriority(QAction.Priority.LowPriority)
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyRight,
                                QIcon(RSRC_PATH + "/textright.png"))
         self._action_align_right = QAction(icon, "&Right", self)
-        self._action_align_right.setShortcut(Qt.CTRL | Qt.Key_R)
+        self._action_align_right.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_R)
         self._action_align_right.setCheckable(True)
-        self._action_align_right.setPriority(QAction.LowPriority)
+        self._action_align_right.setPriority(QAction.Priority.LowPriority)
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyFill,
                                QIcon(RSRC_PATH + "/textjustify.png"))
         self._action_align_justify = QAction(icon, "&Justify", self)
-        self._action_align_justify.setShortcut(Qt.CTRL | Qt.Key_J)
+        self._action_align_justify.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_J)
         self._action_align_justify.setCheckable(True)
-        self._action_align_justify.setPriority(QAction.LowPriority)
+        self._action_align_justify.setPriority(QAction.Priority.LowPriority)
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentMore,
                                QIcon(RSRC_PATH + "/format-indent-more.png"))
         self._action_indent_more = menu.addAction(icon, "&Indent", self.indent)
-        self._action_indent_more.setShortcut(Qt.CTRL | Qt.Key_BracketRight)
-        self._action_indent_more.setPriority(QAction.LowPriority)
+        self._action_indent_more.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_BracketRight)
+        self._action_indent_more.setPriority(QAction.Priority.LowPriority)
         icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentLess,
                                QIcon(RSRC_PATH + "/format-indent-less.png"))
         self._action_indent_less = menu.addAction(icon, "&Unindent",
                                                   self.unindent)
-        self._action_indent_less.setShortcut(Qt.CTRL | Qt.Key_BracketLeft)
-        self._action_indent_less.setPriority(QAction.LowPriority)
+        self._action_indent_less.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_BracketLeft)
+        self._action_indent_less.setPriority(QAction.Priority.LowPriority)
 
         # Make sure the alignLeft is always left of the alignRight
         align_group = QActionGroup(self)
@@ -291,7 +292,7 @@ class TextEdit(QMainWindow):
         menu.addSeparator()
 
         pix = QPixmap(16, 16)
-        pix.fill(Qt.black)
+        pix.fill(Qt.GlobalColor.black)
         self._action_text_color = menu.addAction(pix, "&Color...", self.text_color)
         tb.addAction(self._action_text_color)
 
@@ -306,14 +307,14 @@ class TextEdit(QMainWindow):
                                QIcon(RSRC_PATH + "/checkbox-checked.png"))
         self._action_toggle_check_state = menu.addAction(icon, "Chec&ked")
         self._action_toggle_check_state.toggled.connect(self.set_checked)
-        self._action_toggle_check_state.setShortcut(Qt.CTRL | Qt.Key_K)
+        self._action_toggle_check_state.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_K)
         self._action_toggle_check_state.setCheckable(True)
-        self._action_toggle_check_state.setPriority(QAction.LowPriority)
+        self._action_toggle_check_state.setPriority(QAction.Priority.LowPriority)
         tb.addAction(self._action_toggle_check_state)
 
         tb = self.addToolBar("Format self.actions")
-        tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
-        self.addToolBarBreak(Qt.TopToolBarArea)
+        tb.setAllowedAreas(Qt.ToolBarArea.TopToolBarArea | Qt.ToolBarArea.BottomToolBarArea)
+        self.addToolBarBreak(Qt.ToolBarArea.TopToolBarArea)
         self.addToolBar(tb)
 
         self._combo_style = QComboBox(tb)
@@ -343,7 +344,7 @@ class TextEdit(QMainWindow):
         if not QFile.exists(f):
             return False
         file = QFile(f)
-        if not file.open(QFile.ReadOnly):
+        if not file.open(QFile.OpenModeFlag.ReadOnly):
             return False
 
         data = file.readAll()
@@ -352,7 +353,7 @@ class TextEdit(QMainWindow):
         text = data.data().decode('utf8')
         if mime_type_name == "text/html":
             file_url = QUrl(f) if f[0] == ':' else QUrl.fromLocalFile(f)
-            options = QUrl.FormattingOptions(QUrl.RemoveFilename)
+            options = QUrl.FormattingOptions(QUrl.UrlFormattingOption.RemoveFilename)
             self._text_edit.document().setBaseUrl(file_url.adjusted(options))
             self._text_edit.setHtml(text)
         elif mime_type_name == "text/markdown":
@@ -585,7 +586,7 @@ class TextEdit(QMainWindow):
 
     @Slot()
     def underline_color(self):
-        col = QColorDialog.getColor(Qt.black, self)
+        col = QColorDialog.getColor(Qt.GlobalColor.black, self)
         if not col.isValid():
             return
         fmt = QTextCharFormat()
@@ -596,13 +597,15 @@ class TextEdit(QMainWindow):
     @Slot(QAction)
     def text_align(self, a):
         if a == self._action_align_left:
-            self._text_edit.setAlignment(Qt.AlignLeft | Qt.AlignAbsolute)
+            self._text_edit.setAlignment(Qt.AlignmentFlag.AlignLeft
+                                         | Qt.AlignmentFlag.AlignAbsolute)
         elif a == self._action_align_center:
-            self._text_edit.setAlignment(Qt.AlignHCenter)
+            self._text_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
         elif a == self._action_align_right:
-            self._text_edit.setAlignment(Qt.AlignRight | Qt.AlignAbsolute)
+            self._text_edit.setAlignment(Qt.AlignmentFlag.AlignRight
+                                         | Qt.AlignmentFlag.AlignAbsolute)
         elif a == self._action_align_justify:
-            self._text_edit.setAlignment(Qt.AlignJustify)
+            self._text_edit.setAlignment(Qt.AlignmentFlag.AlignJustify)
 
     @Slot(bool)
     def set_checked(self, checked):
@@ -711,11 +714,11 @@ class TextEdit(QMainWindow):
         self._action_text_color.setIcon(pix)
 
     def alignment_changed(self, a):
-        if a & Qt.AlignLeft:
+        if a & Qt.AlignmentFlag.AlignLeft:
             self._action_align_left.setChecked(True)
-        elif a & Qt.AlignHCenter:
+        elif a & Qt.AlignmentFlag.AlignHCenter:
             self._action_align_center.setChecked(True)
-        elif a & Qt.AlignRight:
+        elif a & Qt.AlignmentFlag.AlignRight:
             self._action_align_right.setChecked(True)
-        elif a & Qt.AlignJustify:
+        elif a & Qt.AlignmentFlag.AlignJustify:
             self._action_align_justify.setChecked(True)
index 9ab7bf8aed60bd857911dfa9c5dc2efbb058d24b..d542ad8937817b3bb1d7f8d8d32dab8ce7a71c24 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/richtext/textobject example from Qt v5.x"""
 
@@ -16,7 +17,7 @@ from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QLineEdit,
 from PySide6.QtSvg import QSvgRenderer
 
 
-SVG_TEXT_FORMAT = QTextFormat.UserObject + 1
+SVG_TEXT_FORMAT = QTextFormat.ObjectTypes.UserObject + 1
 
 
 SVG_DATA = 1
@@ -55,7 +56,7 @@ class Window(QWidget):
         file_name = self._file_name_line_edit.text()
         file = QFile(file_name)
 
-        if not file.open(QIODevice.ReadOnly):
+        if not file.open(QIODevice.OpenModeFlag.ReadOnly):
             reason = file.errorString()
             message = f"Could not open '{file_name}': {reason}"
             QMessageBox.warning(self, "Error Opening File", message)
diff --git a/examples/widgets/thread_signals/doc/thread_signals.png b/examples/widgets/thread_signals/doc/thread_signals.png
new file mode 100644 (file)
index 0000000..ac13aa8
Binary files /dev/null and b/examples/widgets/thread_signals/doc/thread_signals.png differ
diff --git a/examples/widgets/thread_signals/doc/thread_signals.rst b/examples/widgets/thread_signals/doc/thread_signals.rst
new file mode 100644 (file)
index 0000000..e841ec3
--- /dev/null
@@ -0,0 +1,9 @@
+Thread Signals Examples
+=======================
+
+This example demonstrates a PySide6 application that uses threads and signals
+to perform background tasks.
+
+.. image:: thread_signals.png
+    :width: 400
+    :alt: thread_signals screenshot
index ae8e1ba723be5fd09924e1b9dad876340b2d7e50..f9101bd8cc644e456407ece5821b59ff6ff232d8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import QObject, QThread, Signal, Slot
index 261e2619603755deb49494d39cfdb5d61a18402f..ac08ddd32e991fe387044746b2f34717b8a987c7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 PySide6 port of Qt6 example qtbase/examples/widgets/tools/regularexpression
index 2d2bb2bb712958fc83d68b27bc0779aea466f74a..cdca1a8338fa338710192d6b4f1ae304b39eaded 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import re
 import logging
@@ -55,13 +56,13 @@ def codeToPattern(code: str) -> str:
 
 def createHorizontalSeparator() -> QFrame:
     result = QFrame()
-    result.setFrameStyle(QFrame.HLine | QFrame.Sunken)
+    result.setFrameStyle(QFrame.Shape.HLine | QFrame.Shadow.Sunken)
     return result
 
 
 def createVerticalSeparator() -> QFrame:
     result = QFrame()
-    result.setFrameStyle(QFrame.VLine | QFrame.Sunken)
+    result.setFrameStyle(QFrame.Shape.VLine | QFrame.Shadow.Sunken)
     return result
 
 
@@ -101,7 +102,7 @@ class PatternLineEdit(QLineEdit):
 
     def contextMenuEvent(self, event: QContextMenuEvent) -> None:
         menu = self.createStandardContextMenu()
-        menu.setAttribute(Qt.WA_DeleteOnClose)
+        menu.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
         menu.addSeparator()
         self.escapeSelectionAction.setEnabled(self.hasSelectedText())
         menu.addAction(self.escapeSelectionAction)
@@ -117,8 +118,8 @@ class DisplayLineEdit(QLineEdit):
         self.setReadOnly(True)
         self.disablePalette: QPalette = self.palette()
         self.disablePalette.setBrush(
-            QPalette.Base,
-            self.disablePalette.brush(QPalette.Disabled, QPalette.Base),
+            QPalette.ColorRole.Base,
+            self.disablePalette.brush(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base),
         )
         self.setPalette(self.disablePalette)
 
@@ -128,7 +129,7 @@ class DisplayLineEdit(QLineEdit):
         self.copyAction.triggered.connect(
             lambda: QGuiApplication.clipboard().setText(self.text())
         )
-        self.addAction(self.copyAction, QLineEdit.TrailingPosition)
+        self.addAction(self.copyAction, QLineEdit.ActionPosition.TrailingPosition)
 
 
 class RegularExpressionDialog(QDialog):
@@ -166,7 +167,7 @@ class RegularExpressionDialog(QDialog):
 
     def setTextColor(self, widget: QWidget, color: QColor):
         self.palette: QPalette = widget.palette()
-        self.palette.setColor(QPalette.Text, color)
+        self.palette.setColor(QPalette.ColorRole.Text, color)
         widget.setPalette(self.palette)
 
     @Slot()
@@ -182,7 +183,7 @@ class RegularExpressionDialog(QDialog):
 
         self.setTextColor(
             self.patternLineEdit,
-            self.subjectTextEdit.palette().color(QPalette.Text),
+            self.subjectTextEdit.palette().color(QPalette.ColorRole.Text),
         )
         self.matchDetailsTreeWidget.clear()
         self.namedGroupsTreeWidget.clear()
@@ -213,28 +214,28 @@ class RegularExpressionDialog(QDialog):
         matchType: QRegularExpression.MatchType = QRegularExpression.MatchType(
             self.matchTypeComboBox.currentData()
         )
-        patternOptions = QRegularExpression.NoPatternOption
-        matchOptions = QRegularExpression.NoMatchOption
+        patternOptions = QRegularExpression.PatternOption.NoPatternOption
+        matchOptions = QRegularExpression.MatchOption.NoMatchOption
 
         if self.anchoredMatchOptionCheckBox.isChecked():
-            matchOptions |= QRegularExpression.AnchorAtOffsetMatchOption
+            matchOptions |= QRegularExpression.MatchOption.AnchorAtOffsetMatchOption
         if self.dontCheckSubjectStringMatchOptionCheckBox.isChecked():
-            matchOptions |= QRegularExpression.DontCheckSubjectStringMatchOption
+            matchOptions |= QRegularExpression.MatchOption.DontCheckSubjectStringMatchOption
 
         if self.caseInsensitiveOptionCheckBox.isChecked():
-            patternOptions |= QRegularExpression.CaseInsensitiveOption
+            patternOptions |= QRegularExpression.PatternOption.CaseInsensitiveOption
         if self.dotMatchesEverythingOptionCheckBox.isChecked():
-            patternOptions |= QRegularExpression.DotMatchesEverythingOption
+            patternOptions |= QRegularExpression.PatternOption.DotMatchesEverythingOption
         if self.multilineOptionCheckBox.isChecked():
-            patternOptions |= QRegularExpression.MultilineOption
+            patternOptions |= QRegularExpression.PatternOption.MultilineOption
         if self.extendedPatternSyntaxOptionCheckBox.isChecked():
-            patternOptions |= QRegularExpression.ExtendedPatternSyntaxOption
+            patternOptions |= QRegularExpression.PatternOption.ExtendedPatternSyntaxOption
         if self.invertedGreedinessOptionCheckBox.isChecked():
-            patternOptions |= QRegularExpression.InvertedGreedinessOption
+            patternOptions |= QRegularExpression.PatternOption.InvertedGreedinessOption
         if self.dontCaptureOptionCheckBox.isChecked():
-            patternOptions |= QRegularExpression.DontCaptureOption
+            patternOptions |= QRegularExpression.PatternOption.DontCaptureOption
         if self.useUnicodePropertiesOptionCheckBox.isChecked():
-            patternOptions |= QRegularExpression.UseUnicodePropertiesOption
+            patternOptions |= QRegularExpression.PatternOption.UseUnicodePropertiesOption
 
         self.regularExpression.setPatternOptions(patternOptions)
 
@@ -301,7 +302,7 @@ class RegularExpressionDialog(QDialog):
         self.horizontalLayout.addWidget(createVerticalSeparator())
         self.horizontalLayout.addWidget(self.setupInfoUi())
 
-        self._font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
+        self._font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
         self.patternLineEdit.setFont(self._font)
         self.rawStringLiteralLineEdit.setFont(self._font)
         self.escapedPatternLineEdit.setFont(self._font)
@@ -313,7 +314,7 @@ class RegularExpressionDialog(QDialog):
         container = QWidget()
 
         form_layout = QFormLayout(container)
-        form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+        form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
         form_layout.setContentsMargins(QMargins())
 
         form_layout.addRow(QLabel("<h3>Options</h3>"))
@@ -350,15 +351,15 @@ class RegularExpressionDialog(QDialog):
         form_layout.addRow("Match &offset:", self.offsetSpinBox)
 
         self.matchTypeComboBox = QComboBox()
-        self.matchTypeComboBox.addItem("Normal", QRegularExpression.NormalMatch)
+        self.matchTypeComboBox.addItem("Normal", QRegularExpression.MatchType.NormalMatch)
         self.matchTypeComboBox.addItem(
             "Partial prefer complete",
-            QRegularExpression.PartialPreferCompleteMatch,
+            QRegularExpression.MatchType.PartialPreferCompleteMatch,
         )
         self.matchTypeComboBox.addItem(
-            "Partial prefer first", QRegularExpression.PartialPreferFirstMatch
+            "Partial prefer first", QRegularExpression.MatchType.PartialPreferFirstMatch
         )
-        self.matchTypeComboBox.addItem("No match", QRegularExpression.NoMatch)
+        self.matchTypeComboBox.addItem("No match", QRegularExpression.MatchType.NoMatch)
         form_layout.addRow("Match &type:", self.matchTypeComboBox)
 
         self.dontCheckSubjectStringMatchOptionCheckBox = QCheckBox(
@@ -381,7 +382,7 @@ class RegularExpressionDialog(QDialog):
         container = QWidget()
 
         form_layout = QFormLayout(container)
-        form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+        form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
         form_layout.setContentsMargins(QMargins())
 
         self.matchInfoLabel = QLabel("<h3>Match information</h3>")
@@ -392,7 +393,7 @@ class RegularExpressionDialog(QDialog):
         self.matchDetailsTreeWidget.setHeaderLabels(
             ["Match index", "Group index", "Captured string"]
         )
-        self.matchDetailsTreeWidget.setSizeAdjustPolicy(QTreeWidget.AdjustToContents)
+        self.matchDetailsTreeWidget.setSizeAdjustPolicy(QTreeWidget.SizeAdjustPolicy.AdjustToContents)  # noqa: E501
         form_layout.addRow("Match details:", self.matchDetailsTreeWidget)
 
         form_layout.addRow(createHorizontalSeparator())
@@ -406,7 +407,7 @@ class RegularExpressionDialog(QDialog):
 
         self.namedGroupsTreeWidget = QTreeWidget()
         self.namedGroupsTreeWidget.setHeaderLabels(["Index", "Named group"])
-        self.namedGroupsTreeWidget.setSizeAdjustPolicy(QTreeWidget.AdjustToContents)
+        self.namedGroupsTreeWidget.setSizeAdjustPolicy(QTreeWidget.SizeAdjustPolicy.AdjustToContents)  # noqa: E501
         self.namedGroupsTreeWidget.setRootIsDecorated(False)
         form_layout.addRow("Named groups:", self.namedGroupsTreeWidget)
 
@@ -415,7 +416,7 @@ class RegularExpressionDialog(QDialog):
     def setupTextUi(self):
         container = QWidget()
         form_layout = QFormLayout(container)
-        form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+        form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
         form_layout.setContentsMargins(QMargins())
 
         self.regexpAndSubjectLabel = QLabel(
index 648ddea4600d6fa0b85349add4d9ca1b058b0d0d..c3785f416ae444da451da9eddf514dc4ed4bc36e 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -22,7 +23,7 @@ class AddressBook(QWidget):
         main_layout = QGridLayout()
         main_layout.addWidget(name_label, 0, 0)
         main_layout.addWidget(self._name_line, 0, 1)
-        main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+        main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
         main_layout.addWidget(self._address_text, 1, 1)
 
         self.setLayout(main_layout)
index 3c0eb451d303146eca4c096d8778bde31080c3e2..04550871cab695ad4efe975bb7dfb1a1b195a166 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -12,7 +13,7 @@ from PySide6.QtWidgets import (QApplication, QGridLayout,
 
 
 class SortedDict(dict):
-    class Iterator(object):
+    class Iterator:
         def __init__(self, sorted_dict):
             self._dict = sorted_dict
             self._keys = sorted(self._dict.keys())
@@ -67,7 +68,7 @@ class AddressBook(QWidget):
         self._cancel_button.clicked.connect(self.cancel)
 
         button_layout_1 = QVBoxLayout()
-        button_layout_1.addWidget(self._add_button, Qt.AlignTop)
+        button_layout_1.addWidget(self._add_button, Qt.AlignmentFlag.AlignTop)
         button_layout_1.addWidget(self._submit_button)
         button_layout_1.addWidget(self._cancel_button)
         button_layout_1.addStretch()
@@ -75,7 +76,7 @@ class AddressBook(QWidget):
         main_layout = QGridLayout()
         main_layout.addWidget(name_label, 0, 0)
         main_layout.addWidget(self._name_line, 0, 1)
-        main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+        main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
         main_layout.addWidget(self._address_text, 1, 1)
         main_layout.addLayout(button_layout_1, 1, 2)
 
@@ -90,7 +91,7 @@ class AddressBook(QWidget):
         self._address_text.clear()
 
         self._name_line.setReadOnly(False)
-        self._name_line.setFocus(Qt.OtherFocusReason)
+        self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
         self._address_text.setReadOnly(False)
 
         self._add_button.setEnabled(False)
index 611796f5e2fefdd4408996d34811c0f424352a3b..5083afcf359c3d8a1cdf569de45af4232171c494 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -12,7 +13,7 @@ from PySide6.QtWidgets import (QApplication, QGridLayout,
 
 
 class SortedDict(dict):
-    class Iterator(object):
+    class Iterator:
         def __init__(self, sorted_dict):
             self._dict = sorted_dict
             self._keys = sorted(self._dict.keys())
@@ -73,7 +74,7 @@ class AddressBook(QWidget):
         self._previous_button.clicked.connect(self.previous)
 
         button_layout_1 = QVBoxLayout()
-        button_layout_1.addWidget(self._add_button, Qt.AlignTop)
+        button_layout_1.addWidget(self._add_button, Qt.AlignmentFlag.AlignTop)
         button_layout_1.addWidget(self._submit_button)
         button_layout_1.addWidget(self._cancel_button)
         button_layout_1.addStretch()
@@ -85,7 +86,7 @@ class AddressBook(QWidget):
         main_layout = QGridLayout()
         main_layout.addWidget(name_label, 0, 0)
         main_layout.addWidget(self._name_line, 0, 1)
-        main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+        main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
         main_layout.addWidget(self._address_text, 1, 1)
         main_layout.addLayout(button_layout_1, 1, 2)
         main_layout.addLayout(button_layout_2, 3, 1)
@@ -101,7 +102,7 @@ class AddressBook(QWidget):
         self._address_text.clear()
 
         self._name_line.setReadOnly(False)
-        self._name_line.setFocus(Qt.OtherFocusReason)
+        self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
         self._address_text.setReadOnly(False)
 
         self._add_button.setEnabled(False)
index 95f31d46cf29348ae04a7091d16bc548b9c0fb6f..de4bbfacc669c657391e1409e5157f2eb02b5ffc 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -12,7 +13,7 @@ from PySide6.QtWidgets import (QApplication, QGridLayout,
 
 
 class SortedDict(dict):
-    class Iterator(object):
+    class Iterator:
         def __init__(self, sorted_dict):
             self._dict = sorted_dict
             self._keys = sorted(self._dict.keys())
@@ -97,7 +98,7 @@ class AddressBook(QWidget):
         main_layout = QGridLayout()
         main_layout.addWidget(name_label, 0, 0)
         main_layout.addWidget(self._name_line, 0, 1)
-        main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+        main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
         main_layout.addWidget(self._address_text, 1, 1)
         main_layout.addLayout(button_layout_1, 1, 2)
         main_layout.addLayout(button_layout_2, 3, 1)
@@ -230,7 +231,7 @@ class AddressBook(QWidget):
 
         if self._current_mode in (self.AddingMode, self.EditingMode):
             self._name_line.setReadOnly(False)
-            self._name_line.setFocus(Qt.OtherFocusReason)
+            self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
             self._address_text.setReadOnly(False)
 
             self._add_button.setEnabled(False)
index 1e9c05862cc352ddd13425b67b82a8f37eded978..13538207b3c283187d5380dda60394d2a6de0a1f 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -12,7 +13,7 @@ from PySide6.QtWidgets import (QApplication, QDialog, QGridLayout,
 
 
 class SortedDict(dict):
-    class Iterator(object):
+    class Iterator:
         def __init__(self, sorted_dict):
             self._dict = sorted_dict
             self._keys = sorted(self._dict.keys())
@@ -103,7 +104,7 @@ class AddressBook(QWidget):
         main_layout = QGridLayout()
         main_layout.addWidget(name_label, 0, 0)
         main_layout.addWidget(self._name_line, 0, 1)
-        main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+        main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
         main_layout.addWidget(self._address_text, 1, 1)
         main_layout.addLayout(button_layout_1, 1, 2)
         main_layout.addLayout(button_layout_2, 2, 1)
@@ -252,7 +253,7 @@ class AddressBook(QWidget):
 
         if self._current_mode in (self.AddingMode, self.EditingMode):
             self._name_line.setReadOnly(False)
-            self._name_line.setFocus(Qt.OtherFocusReason)
+            self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
             self._address_text.setReadOnly(False)
 
             self._add_button.setEnabled(False)
index d11298fb9dcf0cf1efe2945bccc6441f74ed4d58..73dcd067d05f1523365368746a7f2308d53520e9 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import pickle
 import sys
@@ -13,7 +14,7 @@ from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
 
 
 class SortedDict(dict):
-    class Iterator(object):
+    class Iterator:
         def __init__(self, sorted_dict):
             self._dict = sorted_dict
             self._keys = sorted(self._dict.keys())
@@ -114,7 +115,7 @@ class AddressBook(QWidget):
         main_layout = QGridLayout()
         main_layout.addWidget(name_label, 0, 0)
         main_layout.addWidget(self._name_line, 0, 1)
-        main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+        main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
         main_layout.addWidget(self._address_text, 1, 1)
         main_layout.addLayout(button_layout_1, 1, 2)
         main_layout.addLayout(button_layout_2, 2, 1)
@@ -263,7 +264,7 @@ class AddressBook(QWidget):
 
         if self._current_mode in (self.AddingMode, self.EditingMode):
             self._name_line.setReadOnly(False)
-            self._name_line.setFocus(Qt.OtherFocusReason)
+            self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
             self._address_text.setReadOnly(False)
 
             self._add_button.setEnabled(False)
index 3829c003dfa6276bab8e789c35569bfbdcfa3a49..12e205577fd2b14082e0b3adf6aa87e5400e540e 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import pickle
 import sys
@@ -13,7 +14,7 @@ from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
 
 
 class SortedDict(dict):
-    class Iterator(object):
+    class Iterator:
         def __init__(self, sorted_dict):
             self._dict = sorted_dict
             self._keys = sorted(self._dict.keys())
@@ -120,7 +121,7 @@ class AddressBook(QWidget):
         main_layout = QGridLayout()
         main_layout.addWidget(name_label, 0, 0)
         main_layout.addWidget(self._name_line, 0, 1)
-        main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+        main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
         main_layout.addWidget(self._address_text, 1, 1)
         main_layout.addLayout(button_layout_1, 1, 2)
         main_layout.addLayout(button_layout_2, 2, 1)
@@ -269,7 +270,7 @@ class AddressBook(QWidget):
 
         if self._current_mode in (self.AddingMode, self.EditingMode):
             self._name_line.setReadOnly(False)
-            self._name_line.setFocus(Qt.OtherFocusReason)
+            self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
             self._address_text.setReadOnly(False)
 
             self._add_button.setEnabled(False)
@@ -377,7 +378,7 @@ class AddressBook(QWidget):
 
         out_file = QFile(file_name)
 
-        if not out_file.open(QIODevice.WriteOnly):
+        if not out_file.open(QIODevice.OpenModeFlag.WriteOnly):
             QMessageBox.information(self, "Unable to open file", out_file.errorString())
             return
 
index 62411ace271d082ee37a5c83a719c23236749e89..924069610c234aad37a60ab9b14be77b71cf1ccc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 1
 
index 8649bb5629b95a46f4b57dcd8b0a48bba4797245..0498eb23e5f0ee5ed2b703bff2f56a4303e94c4b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 10
 
@@ -20,7 +21,7 @@ class LCDRange(QWidget):
         super().__init__(parent)
 
         lcd = QLCDNumber(2)
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
 
@@ -92,8 +93,8 @@ class CannonField(QWidget):
 
     def paintEvent(self, event):
         with QPainter(self) as painter:
-            painter.setPen(Qt.NoPen)
-            painter.setBrush(Qt.blue)
+            painter.setPen(Qt.PenStyle.NoPen)
+            painter.setBrush(Qt.GlobalColor.blue)
 
             painter.translate(0, self.height())
             painter.drawPie(QRect(-35, -35, 70, 70), 0, 90 * 16)
@@ -111,7 +112,7 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("&Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
index fbfd2481d32504bf02fef8a56cf8a3b503f8ce56..00ac2fc267fd8b9bef86b52fc4d3260c4ec7d157 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 11
 
@@ -22,7 +23,7 @@ class LCDRange(QWidget):
         super().__init__(parent)
 
         lcd = QLCDNumber(2)
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
 
@@ -127,15 +128,15 @@ class CannonField(QWidget):
                 self.paint_shot(painter)
 
     def paint_shot(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.black)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.black)
         painter.drawRect(self.shot_rect())
 
     barrel_rect = QRect(33, -4, 15, 8)
 
     def paint_cannon(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.blue)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.blue)
 
         painter.save()
         painter.translate(0, self.height())
@@ -173,7 +174,7 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("&Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
@@ -192,7 +193,7 @@ class MyWidget(QWidget):
         cannon_field.force_changed.connect(force.set_value)
 
         shoot = QPushButton("&Shoot")
-        shoot.setFont(QFont("Times", 18, QFont.Bold))
+        shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         shoot.clicked.connect(cannon_field.shoot)
 
index 749c246840eb12380b7bee4e386147119607de05..4960797bcddc791cb5042a29148aa4544fbd2ddf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 12
 
@@ -33,11 +34,11 @@ class LCDRange(QWidget):
 
     def init(self):
         lcd = QLCDNumber(2)
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
         self.label = QLabel()
-        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
+        self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
 
         self.slider.valueChanged.connect(lcd.display)
         self.slider.valueChanged.connect(self.value_changed)
@@ -168,20 +169,20 @@ class CannonField(QWidget):
             self.paint_target(painter)
 
     def paint_shot(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.black)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.black)
         painter.drawRect(self.shot_rect())
 
     def paint_target(self, painter):
-        painter.setPen(Qt.black)
-        painter.setBrush(Qt.red)
+        painter.setPen(Qt.GlobalColor.black)
+        painter.setBrush(Qt.GlobalColor.red)
         painter.drawRect(self.target_rect())
 
     barrel_rect = QRect(33, -4, 15, 8)
 
     def paint_cannon(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.blue)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.blue)
 
         painter.save()
         painter.translate(0, self.height())
@@ -224,7 +225,7 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("&Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
@@ -243,7 +244,7 @@ class MyWidget(QWidget):
         cannon_field.force_changed.connect(force.set_value)
 
         shoot = QPushButton("&Shoot")
-        shoot.setFont(QFont("Times", 18, QFont.Bold))
+        shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         shoot.clicked.connect(cannon_field.shoot)
 
index f9a771d1566ad469d09e3159b588245602563e1b..c9b8bd5d723c2636937f14eb7ec8974655a56798 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 13
 
@@ -34,12 +35,12 @@ class LCDRange(QWidget):
 
     def init(self):
         lcd = QLCDNumber(2)
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
         self.label = QLabel()
-        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
-        self.label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
+        self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
+        self.label.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
 
         self.slider.valueChanged.connect(lcd.display)
         self.slider.valueChanged.connect(self.value_changed)
@@ -184,9 +185,9 @@ class CannonField(QWidget):
     def paintEvent(self, event):
         with QPainter(self) as painter:
             if self._game_ended:
-                painter.setPen(Qt.black)
-                painter.setFont(QFont("Courier", 48, QFont.Bold))
-                painter.drawText(self.rect(), Qt.AlignCenter, "Game Over")
+                painter.setPen(Qt.GlobalColor.black)
+                painter.setFont(QFont("Courier", 48, QFont.Weight.Bold))
+                painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter, "Game Over")
 
             self.paint_cannon(painter)
             if self.is_shooting():
@@ -195,20 +196,20 @@ class CannonField(QWidget):
                 self.paint_target(painter)
 
     def paint_shot(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.black)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.black)
         painter.drawRect(self.shot_rect())
 
     def paint_target(self, painter):
-        painter.setPen(Qt.black)
-        painter.setBrush(Qt.red)
+        painter.setPen(Qt.GlobalColor.black)
+        painter.setBrush(Qt.GlobalColor.red)
         painter.drawRect(self.target_rect())
 
     barrel_rect = QRect(33, -4, 15, 8)
 
     def paint_cannon(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.blue)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.blue)
 
         painter.save()
         painter.translate(0, self.height())
@@ -257,7 +258,7 @@ class GameBoard(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("&Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
@@ -279,13 +280,13 @@ class GameBoard(QWidget):
         self._cannon_field.missed.connect(self.missed)
 
         shoot = QPushButton("&Shoot")
-        shoot.setFont(QFont("Times", 18, QFont.Bold))
+        shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         shoot.clicked.connect(self.fire)
         self._cannon_field.can_shoot.connect(shoot.setEnabled)
 
         restart = QPushButton("&New Game")
-        restart.setFont(QFont("Times", 18, QFont.Bold))
+        restart.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         restart.clicked.connect(self.new_game)
 
index 3c94408f30a6cda9be6a1646a6fab349c68433be..00674189b7baedbc02d2ad1d359d6d2119756fa7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 14
 
@@ -35,12 +36,12 @@ class LCDRange(QWidget):
 
     def init(self):
         lcd = QLCDNumber(2)
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
         self.label = QLabel()
-        self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
-        self.label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
+        self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
+        self.label.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
 
         self.slider.valueChanged.connect(lcd.display)
         self.slider.valueChanged.connect(self.value_changed)
@@ -185,7 +186,7 @@ class CannonField(QWidget):
         self.update(region)
 
     def mousePressEvent(self, event):
-        if event.button() != Qt.LeftButton:
+        if event.button() != Qt.MouseButton.LeftButton:
             return
         if self.barrel_hit(event.position().toPoint()):
             self._barrel_pressed = True
@@ -202,15 +203,15 @@ class CannonField(QWidget):
         self.set_angle(round(rad * 180 / math.pi))
 
     def mouseReleaseEvent(self, event):
-        if event.button() == Qt.LeftButton:
+        if event.button() == Qt.MouseButton.LeftButton:
             self._barrel_pressed = False
 
     def paintEvent(self, event):
         with QPainter(self) as painter:
             if self._game_ended:
-                painter.setPen(Qt.black)
-                painter.setFont(QFont("Courier", 48, QFont.Bold))
-                painter.drawText(self.rect(), Qt.AlignCenter, "Game Over")
+                painter.setPen(Qt.GlobalColor.black)
+                painter.setFont(QFont("Courier", 48, QFont.Weight.Bold))
+                painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter, "Game Over")
 
             self.paint_cannon(painter)
             self.paint_barrier(painter)
@@ -220,25 +221,25 @@ class CannonField(QWidget):
                 self.paint_target(painter)
 
     def paint_shot(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.black)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.black)
         painter.drawRect(self.shot_rect())
 
     def paint_target(self, painter):
-        painter.setPen(Qt.black)
-        painter.setBrush(Qt.red)
+        painter.setPen(Qt.GlobalColor.black)
+        painter.setBrush(Qt.GlobalColor.red)
         painter.drawRect(self.target_rect())
 
     def paint_barrier(self, painter):
-        painter.setPen(Qt.black)
-        painter.setBrush(Qt.yellow)
+        painter.setPen(Qt.GlobalColor.black)
+        painter.setBrush(Qt.GlobalColor.yellow)
         painter.drawRect(self.barrier_rect())
 
     barrel_rect = QRect(33, -4, 15, 8)
 
     def paint_cannon(self, painter):
-        painter.setPen(Qt.NoPen)
-        painter.setBrush(Qt.blue)
+        painter.setPen(Qt.PenStyle.NoPen)
+        painter.setBrush(Qt.GlobalColor.blue)
 
         painter.save()
         painter.translate(0, self.height())
@@ -300,7 +301,7 @@ class GameBoard(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("&Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
@@ -311,7 +312,7 @@ class GameBoard(QWidget):
         force.set_range(10, 50)
 
         cannon_box = QFrame()
-        cannon_box.setFrameStyle(QFrame.WinPanel | QFrame.Sunken)
+        cannon_box.setFrameStyle(QFrame.Shape.WinPanel | QFrame.Shadow.Sunken)
 
         self._cannon_field = CannonField()
 
@@ -325,13 +326,13 @@ class GameBoard(QWidget):
         self._cannon_field.missed.connect(self.missed)
 
         shoot = QPushButton("&Shoot")
-        shoot.setFont(QFont("Times", 18, QFont.Bold))
+        shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         shoot.clicked.connect(self.fire)
         self._cannon_field.can_shoot.connect(shoot.setEnabled)
 
         restart = QPushButton("&New Game")
-        restart.setFont(QFont("Times", 18, QFont.Bold))
+        restart.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         restart.clicked.connect(self.new_game)
 
@@ -340,9 +341,9 @@ class GameBoard(QWidget):
         hits_label = QLabel("HITS")
         shots_left_label = QLabel("SHOTS LEFT")
 
-        QShortcut(QKeySequence(Qt.Key_Enter), self, self.fire)
-        QShortcut(QKeySequence(Qt.Key_Return), self, self.fire)
-        QShortcut(QKeySequence(Qt.CTRL | Qt.Key_Q), self, self.close)
+        QShortcut(QKeySequence(Qt.Key.Key_Enter), self, self.fire)
+        QShortcut(QKeySequence(Qt.Key.Key_Return), self, self.fire)
+        QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q), self, self.close)
 
         top_layout = QHBoxLayout()
         top_layout.addWidget(shoot)
index d3adba3968ba868682005b57c504e3ce3e295637..2b441391a8d9e3bef339abbb13983c4edcc8063f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 2
 
@@ -15,7 +16,7 @@ if __name__ == '__main__':
 
     quit = QPushButton("Quit")
     quit.resize(75, 30)
-    quit.setFont(QFont("Times", 18, QFont.Bold))
+    quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
     quit.clicked.connect(app.quit)
 
index 13bd8f736cc269f55a3f2ca68cf591794cd4ad45..9befa772cebbeff24910f39da29c1b595ee9efdc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 3
 
@@ -17,7 +18,7 @@ if __name__ == '__main__':
     window.resize(200, 120)
 
     quit = QPushButton("Quit", window)
-    quit.setFont(QFont("Times", 18, QFont.Bold))
+    quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
     quit.setGeometry(10, 40, 180, 40)
     quit.clicked.connect(app.quit)
 
index 37a2dc9dd313bb319dedfc5b7a7fdcd531857918..9f11de3594d4dbcf253313c6ed9562790e558a57 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 4
 
@@ -18,7 +19,7 @@ class MyWidget(QWidget):
 
         self.quit = QPushButton("Quit", self)
         self.quit.setGeometry(62, 40, 75, 30)
-        self.quit.setFont(QFont("Times", 18, QFont.Bold))
+        self.quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         self.quit.clicked.connect(qApp.quit)  # noqa: F821
 
index ed5d085f86741d79d0d124826b86ce33ffe4f26f..12f4847c810c324269192701d49bc4361b66fa6e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 5
 
@@ -17,11 +18,11 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         lcd = QLCDNumber(2)
 
-        slider = QSlider(Qt.Horizontal)
+        slider = QSlider(Qt.Orientation.Horizontal)
         slider.setRange(0, 99)
         slider.setValue(0)
 
index ea2e044e6ca186a2e02813ab9c26b2f9b55ba7e6..04db4b51e4a33a56d2b02439fd0611e872ca5343 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 6
 
@@ -17,7 +18,7 @@ class LCDRange(QWidget):
         super().__init__(parent)
 
         lcd = QLCDNumber(2)
-        slider = QSlider(Qt.Horizontal)
+        slider = QSlider(Qt.Orientation.Horizontal)
         slider.setRange(0, 99)
         slider.setValue(0)
         slider.valueChanged.connect(lcd.display)
@@ -32,7 +33,7 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
         layout = QVBoxLayout(self)
index 1175107b87389bc3a21980330a59969c0979ca8c..2ef051e21b3009242bc7ad61af88558570afbddb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 7
 
@@ -21,7 +22,7 @@ class LCDRange(QWidget):
 
         lcd = QLCDNumber(2)
 
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
 
@@ -45,7 +46,7 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
         previous_range = None
index 9bb5516b8e26ed21f647cf7ddf4da2df44e6dcce..560d73cffd211699bd8be46c5445e0f891e17915 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 8
 
@@ -20,7 +21,7 @@ class LCDRange(QWidget):
         super().__init__(parent)
 
         lcd = QLCDNumber(2)
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
 
@@ -86,7 +87,7 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
index 7cdda4e7b6671696f03f09091c045151c68b5a51..25d2554fd0e2dc8a117032934c9322bc32177eda 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 # PySide6 tutorial 9
 
@@ -20,7 +21,7 @@ class LCDRange(QWidget):
         super().__init__(parent)
 
         lcd = QLCDNumber(2)
-        self.slider = QSlider(Qt.Horizontal)
+        self.slider = QSlider(Qt.Orientation.Horizontal)
         self.slider.setRange(0, 99)
         self.slider.setValue(0)
 
@@ -78,8 +79,8 @@ class CannonField(QWidget):
 
     def paintEvent(self, event):
         with QPainter(self) as painter:
-            painter.setPen(Qt.NoPen)
-            painter.setBrush(Qt.blue)
+            painter.setPen(Qt.PenStyle.NoPen)
+            painter.setBrush(Qt.GlobalColor.blue)
 
             painter.translate(0, self.rect().height())
             painter.drawPie(QRect(-35, -35, 70, 70), 0, 90 * 16)
@@ -92,7 +93,7 @@ class MyWidget(QWidget):
         super().__init__(parent)
 
         quit = QPushButton("Quit")
-        quit.setFont(QFont("Times", 18, QFont.Bold))
+        quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
 
         quit.clicked.connect(qApp.quit)  # noqa: F821
 
index 9dc923260afd6950a932f9e953db26fc1e4bb409..92402eb26043fc25a9e193b5e061c1b79cd36290 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -20,8 +21,8 @@ class MyModel(QAbstractTableModel):
     def columnCount(self, parent=None):
         return 3
 
-    def data(self, index, role=Qt.DisplayRole):
-        if role == Qt.DisplayRole:
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+        if role == Qt.ItemDataRole.DisplayRole:
             row = index.row() + 1
             column = index.column() + 1
             return f"Row{row}, Column{column}"
index f39ec462cbabd7c2f596b9815f4f8ccf07b8ff9c..6fef1d596c69286735b52168022392a44192882e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -21,36 +22,36 @@ class MyModel(QAbstractTableModel):
         return 3
 
 #! [1]
-    def data(self, index, role=Qt.DisplayRole):
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
         row = index.row()
         col = index.column()
         # generate a log message when this method gets called
         print(f"row {row}, col{col}, role {role}")
 
-        if role == Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             if row == 0 and col == 1:
                 return "<--left"
             if row == 1 and col == 1:
                 return "right-->"
             return f"Row{row}, Column{col + 1}"
 
-        elif role == Qt.FontRole:
+        elif role == Qt.ItemDataRole.FontRole:
             if row == 0 and col == 0:  # change font only for cell(0,0)
                 bold_font = QFont()
                 bold_font.setBold(True)
                 return bold_font
 
-        elif role == Qt.BackgroundRole:
+        elif role == Qt.ItemDataRole.BackgroundRole:
             if row == 1 and col == 2:  # change background only for cell(1,2)
-                return QBrush(Qt.red)
+                return QBrush(Qt.GlobalColor.red)
 
-        elif role == Qt.TextAlignmentRole:
+        elif role == Qt.ItemDataRole.TextAlignmentRole:
             if row == 1 and col == 1:  # change text alignment only for cell(1,1)
-                return Qt.AlignRight | Qt.AlignVCenter
+                return Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter
 
-        elif role == Qt.CheckStateRole:
+        elif role == Qt.ItemDataRole.CheckStateRole:
             if row == 1 and col == 0:  # add a checkbox to cell(1,0)
-                return Qt.Checked
+                return Qt.CheckState.Checked
 
         return None
 #! [1]
index 2148ec5d36b09b320d4fb26744d888fc64e963aa..2519402218da848dd26fd983a8b20eeaf216b265 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -26,10 +27,10 @@ class MyModel(QAbstractTableModel):
         return 3
 
 #! [2]
-    def data(self, index, role=Qt.DisplayRole):
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
         row = index.row()
         col = index.column()
-        if role == Qt.DisplayRole and row == 0 and col == 0:
+        if role == Qt.ItemDataRole.DisplayRole and row == 0 and col == 0:
             return QTime.currentTime().toString()
         return None
 #! [2]
@@ -40,7 +41,7 @@ class MyModel(QAbstractTableModel):
         # we identify the top left cell
         top_left = self.createIndex(0, 0)
         # emit a signal to make the view reread identified data
-        self.dataChanged.emit(top_left, top_left, [Qt.DisplayRole])
+        self.dataChanged.emit(top_left, top_left, [Qt.ItemDataRole.DisplayRole])
 #! [3]
 
 
index 3feef17bf8928136670eb7835e207d3537d0ab7e..12815abf3b0ed692bc9f8233842348c9093a1824 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -19,8 +20,8 @@ class MyModel(QAbstractTableModel):
     def columnCount(self, parent=None):
         return 3
 
-    def data(self, index, role=Qt.DisplayRole):
-        if role == Qt.DisplayRole:
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+        if role == Qt.ItemDataRole.DisplayRole:
             row = index.row() + 1
             column = index.column() + 1
             return f"Row{row}, Column{column}"
@@ -28,7 +29,7 @@ class MyModel(QAbstractTableModel):
 
 #! [1]
     def headerData(self, section, orientation, role):
-        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
+        if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
             return ["first", "second", "third"][section]
         return None
 #! [1]
index 1a4481fc939540240f71c050dde0829e41f69aea..ae57634b12121a2d39ab37f603c70e7f8953867e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from itertools import chain
@@ -28,14 +29,14 @@ class MyModel(QAbstractTableModel):
     def columnCount(self, parent=None):
         return COLS
 
-    def data(self, index, role=Qt.DisplayRole):
-        if role == Qt.DisplayRole and self.checkIndex(index):
+    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+        if role == Qt.ItemDataRole.DisplayRole and self.checkIndex(index):
             return self._grid_data[index.row()][index.column()]
         return None
 
 #! [1]
     def setData(self, index, value, role):
-        if role != Qt.EditRole or not self.checkIndex(index):
+        if role != Qt.ItemDataRole.EditRole or not self.checkIndex(index):
             return False
         # save value from editor to member m_gridData
         self._grid_data[index.row()][index.column()] = value
index cac3c6d5372e880c8ae2d4d07d3fbe53b79e62db..d0259f4853fa5583ea91d484ccc1cda046486a07 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index c879d8f67769cb67df1891dd1e42b15026ce467f..180fd9b11a36d131af0f057fe16f5d846389e712 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -53,7 +54,7 @@ class MainWindow(QMainWindow):
     def selection_changed_slot(self, new_selection, old_selection):
         # get the text of the selected item
         index = self._tree_view.selectionModel().currentIndex()
-        selected_text = index.data(Qt.DisplayRole)
+        selected_text = index.data(Qt.ItemDataRole.DisplayRole)
         # find out the hierarchy level of the selected item
         hierarchy_level = 1
         seek_root = index
index 0f01f96842e873a13b112114ad843bb91fdbe565..91c040814e75596ed87efb77b0fe00d6a0276049 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from textwrap import dedent
 
@@ -83,7 +84,7 @@ class CharacterWidget(QWidget):
         QToolTip.showText(event.globalPosition().toPoint(), text, self)
 
     def mousePressEvent(self, event):
-        if event.button() == Qt.LeftButton:
+        if event.button() == Qt.MouseButton.LeftButton:
             self._last_key = self._unicode_from_pos(event.position().toPoint())
             if self._last_key != -1:
                 c = chr(self._last_key)
@@ -98,14 +99,14 @@ class CharacterWidget(QWidget):
 
     def render(self, event, painter):
         painter = QPainter(self)
-        painter.fillRect(event.rect(), QBrush(Qt.white))
+        painter.fillRect(event.rect(), QBrush(Qt.GlobalColor.white))
         painter.setFont(self._display_font)
         redraw_rect = event.rect()
         begin_row = int(redraw_rect.top() / self._square_size)
         end_row = int(redraw_rect.bottom() / self._square_size)
         begin_column = int(redraw_rect.left() / self._square_size)
         end_column = int(redraw_rect.right() / self._square_size)
-        painter.setPen(QPen(Qt.gray))
+        painter.setPen(QPen(Qt.GlobalColor.gray))
         for row in range(begin_row, end_row + 1):
             for column in range(begin_column, end_column + 1):
                 x = int(column * self._square_size)
@@ -113,7 +114,7 @@ class CharacterWidget(QWidget):
                 painter.drawRect(x, y, self._square_size, self._square_size)
 
         font_metrics = QFontMetrics(self._display_font)
-        painter.setPen(QPen(Qt.black))
+        painter.setPen(QPen(Qt.GlobalColor.black))
         for row in range(begin_row, end_row + 1):
             for column in range(begin_column, end_column + 1):
                 key = int(row * COLUMNS + column)
@@ -124,7 +125,8 @@ class CharacterWidget(QWidget):
                 if key == self._last_key:
                     painter.fillRect(column * self._square_size + 1,
                                      row * self._square_size + 1,
-                                     self._square_size, self._square_size, QBrush(Qt.red))
+                                     self._square_size, self._square_size,
+                                     QBrush(Qt.GlobalColor.red))
 
                 text = chr(key)
                 painter.drawText(column * self._square_size + (self._square_size / 2)
index aa874884f05d650993a02d950ff004fdf6843f23..9763008e2c92e9a81b9cd08da59c5069e51392d6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, qVersion, qFuzzyCompare
 from PySide6.QtGui import QGuiApplication, QFontDatabase
@@ -23,7 +24,7 @@ class FontInfoDialog(QDialog):
         text_edit.setReadOnly(True)
         text_edit.setFont(QFontDatabase.systemFont(QFontDatabase.FixedFont))
         main_layout.addWidget(text_edit)
-        button_box = QDialogButtonBox(QDialogButtonBox.Close, self)
+        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, self)
         button_box.rejected.connect(self.reject)
         main_layout.addWidget(button_box)
 
index e84a1d8afab453b64ab4c5601048a19b9f171b9d..8feded90a09e5fface629440e18905a035195195 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index d79285def79494c0a940b8292d7f8a454193c582..88d738f8ba1908549a39960102903d157708349a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, QSignalBlocker, Slot
 from PySide6.QtGui import QGuiApplication, QClipboard, QFont, QFontDatabase
@@ -36,10 +37,10 @@ class MainWindow(QMainWindow):
 
         self._filter_label = QLabel("Filter:")
         self._filter_combo = QComboBox()
-        self._filter_combo.addItem("All", int(QFontComboBox.AllFonts.value))
-        self._filter_combo.addItem("Scalable", int(QFontComboBox.ScalableFonts.value))
-        self._filter_combo.addItem("Monospaced", int(QFontComboBox.MonospacedFonts.value))
-        self._filter_combo.addItem("Proportional", int(QFontComboBox.ProportionalFonts.value))
+        self._filter_combo.addItem("All", QFontComboBox.FontFilter.AllFonts)
+        self._filter_combo.addItem("Scalable", QFontComboBox.FontFilter.ScalableFonts)
+        self._filter_combo.addItem("Monospaced", QFontComboBox.FontFilter.MonospacedFonts)
+        self._filter_combo.addItem("Proportional", QFontComboBox.FontFilter.ProportionalFonts)
         self._filter_combo.setCurrentIndex(0)
         self._filter_combo.currentIndexChanged.connect(self.filter_changed)
 
@@ -116,7 +117,7 @@ class MainWindow(QMainWindow):
 
     @Slot(int)
     def filter_changed(self, f):
-        filter = QFontComboBox.FontFilter(self._filter_combo.itemData(f))
+        filter = self._filter_combo.itemData(f)
         self._font_combo.setFontFilters(filter)
         count = self._font_combo.count()
         self.statusBar().showMessage(f"{count} font(s) found")
@@ -162,6 +163,6 @@ class MainWindow(QMainWindow):
         screen_geometry = self.screen().geometry()
         dialog = FontInfoDialog(self)
         dialog.setWindowTitle("Fonts")
-        dialog.setAttribute(Qt.WA_DeleteOnClose)
+        dialog.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
         dialog.resize(screen_geometry.width() / 4, screen_geometry.height() / 4)
         dialog.show()
index f0030b356643affdf783f34d60cf9f4344bb6cfd..fba0a43749d510f26dd41a255e3009e4ea021772 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 import sys
 
 from PySide6.QtCore import QTime, QTimer, Slot
@@ -9,7 +10,7 @@ from PySide6.QtWidgets import QApplication, QLCDNumber
 class DigitalClock(QLCDNumber):
     def __init__(self, parent=None):
         super().__init__(parent)
-        self.setSegmentStyle(QLCDNumber.Filled)
+        self.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
         self.setDigitCount(8)
 
         self.timer = QTimer(self)
index b5df2aa3532b8c77298bdc36b22a9ff920483d05..4428b332e1bb35b471350f161d9e3ec14b355ec2 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/widgets/tetrix example from Qt v5.x"""
 
@@ -32,23 +33,23 @@ class TetrixWindow(QWidget):
         self.board = TetrixBoard()
 
         next_piece_label = QLabel()
-        next_piece_label.setFrameStyle(QFrame.Box | QFrame.Raised)
-        next_piece_label.setAlignment(Qt.AlignCenter)
+        next_piece_label.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Raised)
+        next_piece_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
         self.board.set_next_piece_label(next_piece_label)
 
         score_lcd = QLCDNumber(5)
-        score_lcd.setSegmentStyle(QLCDNumber.Filled)
+        score_lcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
         level_lcd = QLCDNumber(2)
-        level_lcd.setSegmentStyle(QLCDNumber.Filled)
+        level_lcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
         lines_lcd = QLCDNumber(5)
-        lines_lcd.setSegmentStyle(QLCDNumber.Filled)
+        lines_lcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
 
         start_button = QPushButton("&Start")
-        start_button.setFocusPolicy(Qt.NoFocus)
+        start_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
         quit_button = QPushButton("&Quit")
-        quit_button.setFocusPolicy(Qt.NoFocus)
+        quit_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
         pause_button = QPushButton("&Pause")
-        pause_button.setFocusPolicy(Qt.NoFocus)
+        pause_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
 
         start_button.clicked.connect(self.board.start)
         pause_button.clicked.connect(self.board.pause)
@@ -76,7 +77,7 @@ class TetrixWindow(QWidget):
 
     def create_label(self, text):
         lbl = QLabel(text)
-        lbl.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
+        lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom)
         return lbl
 
 
@@ -106,8 +107,8 @@ class TetrixBoard(QFrame):
         self.level = 0
         self.board = None
 
-        self.setFrameStyle(QFrame.Panel | QFrame.Sunken)
-        self.setFocusPolicy(Qt.StrongFocus)
+        self.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
+        self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
         self._is_started = False
         self._is_paused = False
         self.clear_board()
@@ -180,7 +181,7 @@ class TetrixBoard(QFrame):
             rect = self.contentsRect()
 
             if self._is_paused:
-                painter.drawText(rect, Qt.AlignCenter, "Pause")
+                painter.drawText(rect, Qt.AlignmentFlag.AlignCenter, "Pause")
                 return
 
             board_top = rect.bottom() - TetrixBoard.board_height * self.square_height()
@@ -208,17 +209,17 @@ class TetrixBoard(QFrame):
             return
 
         key = event.key()
-        if key == Qt.Key_Left:
+        if key == Qt.Key.Key_Left:
             self.try_move(self._cur_piece, self._cur_x - 1, self._cur_y)
-        elif key == Qt.Key_Right:
+        elif key == Qt.Key.Key_Right:
             self.try_move(self._cur_piece, self._cur_x + 1, self._cur_y)
-        elif key == Qt.Key_Down:
+        elif key == Qt.Key.Key_Down:
             self.try_move(self._cur_piece.rotated_right(), self._cur_x, self._cur_y)
-        elif key == Qt.Key_Up:
+        elif key == Qt.Key.Key_Up:
             self.try_move(self._cur_piece.rotated_left(), self._cur_x, self._cur_y)
-        elif key == Qt.Key_Space:
+        elif key == Qt.Key.Key_Space:
             self.drop_down()
-        elif key == Qt.Key_D:
+        elif key == Qt.Key.Key_D:
             self.one_line_down()
         else:
             super(TetrixBoard, self).keyPressEvent(event)
@@ -367,7 +368,7 @@ class TetrixBoard(QFrame):
                          y + self.square_height() - 1, x + self.square_width() - 1, y + 1)
 
 
-class TetrixPiece(object):
+class TetrixPiece:
     coords_table = (
         ((0, 0), (0, 0), (0, 0), (0, 0)),
         ((0, -1), (0, 0), (-1, 0), (-1, 1)),
index e40077a38fcd58ce2786db81b3445d22e9db1ffb..ec2b5d651386633d61f34d51b3fe5efbeac521b4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the widgets/gallery example from Qt v5.15"""
 
index d43ab26a59017c358bccde107812d9d59233a729..972ffbaa06c85ace554473ece01f2052c1a36c1c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
@@ -153,7 +154,7 @@ class WidgetGallery(QDialog):
         disable_widgets_checkbox.toggled.connect(simple_input_widgets_groupbox.setDisabled)
 
         help_shortcut = QShortcut(self)
-        help_shortcut.setKey(QKeySequence.HelpContents)
+        help_shortcut.setKey(QKeySequence.StandardKey.HelpContents)
         help_shortcut.activated.connect(self.help_on_current_widget)
 
         top_layout = QHBoxLayout()
@@ -164,8 +165,8 @@ class WidgetGallery(QDialog):
         top_layout.addStretch(1)
         top_layout.addWidget(disable_widgets_checkbox)
 
-        dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.Help
-                                            | QDialogButtonBox.Close)
+        dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.StandardButton.Help
+                                            | QDialogButtonBox.StandardButton.Close)
         init_widget(dialog_buttonbox, "dialogButtonBox")
         dialog_buttonbox.helpRequested.connect(launch_module_help)
         dialog_buttonbox.rejected.connect(self.reject)
@@ -223,7 +224,7 @@ class WidgetGallery(QDialog):
         init_widget(menu_toolbutton, "menuButton")
         menu_toolbutton.setText("Menu Button")
         tool_menu = QMenu(menu_toolbutton)
-        menu_toolbutton.setPopupMode(QToolButton.InstantPopup)
+        menu_toolbutton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
         tool_menu.addAction("Option")
         tool_menu.addSeparator()
         action = tool_menu.addAction("Checkable Option")
@@ -256,7 +257,7 @@ class WidgetGallery(QDialog):
         checkbox = QCheckBox("Tri-state check box")
         init_widget(checkbox, "checkBox")
         checkbox.setTristate(True)
-        checkbox.setCheckState(Qt.PartiallyChecked)
+        checkbox.setCheckState(Qt.CheckState.PartiallyChecked)
 
         checkable_layout = QVBoxLayout()
         checkable_layout.addWidget(radiobutton_1)
@@ -299,7 +300,7 @@ class WidgetGallery(QDialog):
     def create_itemview_tabwidget(self):
         result = QTabWidget()
         init_widget(result, "bottomLeftTabWidget")
-        result.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Ignored)
+        result.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Ignored)
 
         tree_view = QTreeView()
         init_widget(tree_view, "treeView")
@@ -324,7 +325,7 @@ class WidgetGallery(QDialog):
         icon_mode_listview = QListView()
         init_widget(icon_mode_listview, "iconModeListView")
 
-        icon_mode_listview.setViewMode(QListView.IconMode)
+        icon_mode_listview.setViewMode(QListView.ViewMode.IconMode)
         icon_mode_listview.setModel(list_model)
 
         result.addTab(embed_into_hbox_layout(tree_view), "Tree View")
@@ -343,7 +344,7 @@ class WidgetGallery(QDialog):
         lineedit = QLineEdit("s3cRe7")
         init_widget(lineedit, "lineEdit")
         lineedit.setClearButtonEnabled(True)
-        lineedit.setEchoMode(QLineEdit.Password)
+        lineedit.setEchoMode(QLineEdit.EchoMode.Password)
 
         spin_box = QSpinBox()
         init_widget(spin_box, "spinBox")
@@ -355,12 +356,12 @@ class WidgetGallery(QDialog):
 
         slider = QSlider()
         init_widget(slider, "slider")
-        slider.setOrientation(Qt.Horizontal)
+        slider.setOrientation(Qt.Orientation.Horizontal)
         slider.setValue(40)
 
         scrollbar = QScrollBar()
         init_widget(scrollbar, "scrollBar")
-        scrollbar.setOrientation(Qt.Horizontal)
+        scrollbar.setOrientation(Qt.Orientation.Horizontal)
         scrollbar.setValue(60)
 
         dial = QDial()
index a35aeb0f2ff7a1c48cceffebd3abaa4e0174a678..f5afdeef82dec7fac3e0ae1e1d86abaa1274fcd7 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2013 Riverbank Computing Limited.
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """PySide6 port of the xml/dombookmarks example from Qt v5.x"""
 
@@ -37,7 +38,7 @@ class MainWindow(QMainWindow):
             return
 
         in_file = QFile(file_name)
-        if not in_file.open(QFile.ReadOnly | QFile.Text):
+        if not in_file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
             reason = in_file.errorString()
             QMessageBox.warning(self, "DOM Bookmarks",
                                 f"Cannot read file {file_name}:\n{reason}.")
@@ -55,7 +56,7 @@ class MainWindow(QMainWindow):
             return
 
         out_file = QFile(file_name)
-        if not out_file.open(QFile.WriteOnly | QFile.Text):
+        if not out_file.open(QFile.OpenModeFlag.WriteOnly | QFile.Text):
             reason = out_file.errorString()
             QMessageBox.warning(self, "DOM Bookmarks",
                                 f"Cannot write file {file_name}:\n{reason}.")
@@ -72,14 +73,14 @@ class MainWindow(QMainWindow):
     def create_menus(self):
         self._file_menu = self.menuBar().addMenu("&File")
         self._file_menu.addAction(QAction("&Open...", self,
-                                          shortcut=QKeySequence(
-                                              Qt.CTRL | Qt.Key_O), triggered=self.open))
+                                          shortcut=QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_O),
+                                          triggered=self.open))
         self._file_menu.addAction(QAction("&Save As...", self,
-                                          shortcut=QKeySequence(
-                                              Qt.CTRL | Qt.Key_S), triggered=self.save_as))
+                                          shortcut=QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_S),
+                                          triggered=self.save_as))
         self._file_menu.addAction(QAction("E&xit", self,
-                                          shortcut=QKeySequence(
-                                              Qt.CTRL | Qt.Key_Q), triggered=self.close))
+                                          shortcut=QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q),
+                                          triggered=self.close))
 
         self.menuBar().addSeparator()
 
@@ -92,7 +93,7 @@ class XbelTree(QTreeWidget):
     def __init__(self, parent=None):
         super().__init__(parent)
 
-        self.header().setSectionResizeMode(QHeaderView.Stretch)
+        self.header().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
         self.setHeaderLabels(("Title", "Location"))
 
         self._dom_document = QDomDocument()
@@ -102,11 +103,12 @@ class XbelTree(QTreeWidget):
         self._folder_icon = QIcon()
         self._bookmark_icon = QIcon()
 
-        self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirClosedIcon),
-                                    QIcon.Normal, QIcon.Off)
-        self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirOpenIcon),
-                                    QIcon.Normal, QIcon.On)
-        self._bookmark_icon.addPixmap(self.style().standardPixmap(QStyle.SP_FileIcon))
+        style = self.style()
+        self._folder_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirClosedIcon),
+                                    QIcon.Mode.Normal, QIcon.State.Off)
+        self._folder_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirOpenIcon),
+                                    QIcon.Mode.Normal, QIcon.State.On)
+        self._bookmark_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_FileIcon))
 
     def read(self, device):
         ok, errorStr, errorLine, errorColumn = self._dom_document.setContent(device, True)
@@ -172,7 +174,7 @@ class XbelTree(QTreeWidget):
         if not title:
             title = "Folder"
 
-        item.setFlags(item.flags() | Qt.ItemIsEditable)
+        item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable)
         item.setIcon(0, self._folder_icon)
         item.setText(0, title)
 
@@ -190,13 +192,13 @@ class XbelTree(QTreeWidget):
                 if not title:
                     title = "Folder"
 
-                child_item.setFlags(item.flags() | Qt.ItemIsEditable)
+                child_item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable)
                 child_item.setIcon(0, self._bookmark_icon)
                 child_item.setText(0, title)
                 child_item.setText(1, child.attribute('href'))
             elif child.tagName() == 'separator':
                 child_item = self.create_item(child, item)
-                child_item.setFlags(item.flags() & ~(Qt.ItemIsSelectable | Qt.ItemIsEditable))
+                child_item.setFlags(item.flags() & ~(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEditable))  # noqa: E501
                 child_item.setText(0, 30 * "\xb7")
 
             child = child.nextSiblingElement()
diff --git a/requirements-coin.txt b/requirements-coin.txt
new file mode 100644 (file)
index 0000000..5ee2bac
--- /dev/null
@@ -0,0 +1,12 @@
+pip>=24.2
+setuptools==72.1.0
+importlib_metadata>=6
+importlib_resources>=5.10.2
+packaging>=24
+ordered-set>=3.1.1
+more_itertools>=8.8
+jaraco.text>=3.7
+importlib_metadata>=6
+tomli>=2.0.1
+wheel>=0.43.0
+platformdirs >= 2.6.2
index 7205ef57fee54bcc9c39aaea37c5f9fa456ef233..7e795c4f5fcedd198d30fd37abbcd9d43efb8d03 100644 (file)
@@ -1,12 +1,13 @@
-sphinx==7.2.6
-sphinx-design==0.5.0
+sphinx==7.4.7
+sphinx-design==0.6.0
 sphinx-copybutton==0.5.2
-sphinx-tags==0.3.1
-sphinx-toolbox
-myst-parser==2.0.0
+sphinx-tags==0.4
+sphinx-toolbox==3.7.0
+sphinx-reredirects==0.1.5
+myst-parser==3.0.1
 # FIXME: Using fork in order to enable the 'collapse_navbar=True'
 # option for the sphinx-theme. Upstream proposal:
 # https://github.com/pradyunsg/furo/pull/748#issuecomment-1895448722
 # furo==2023.9.10
 furo @ git+https://github.com/cmaureir/furo@add_collapse
-graphviz==0.20
+graphviz==0.20.3
index 65380e93b28a6cdedb905a33b25da5359b1d32e8..9769977f1334f18beed2ea7c1b91abd48ebdbe7e 100644 (file)
@@ -1,13 +1,11 @@
 # Build dependencies
-setuptools==69.1.1
-packaging==23.2
-build==1.0.3
-wheel==0.42.0
+setuptools==72.1.0
+packaging==24.1
+build==1.2.1
+wheel==0.43.0
 distro==1.9.0; sys_platform == 'linux'
 patchelf==0.17.2; sys_platform == 'linux'
-# 1.24.4 is the last version that supports Python 3.8
-numpy<1.25; python_version < '3.9'
-numpy==1.26.3; python_version >= '3.9'
-
-# For examples
-PyOpenGL
+# 2.0.2 is the last version that supports Python 3.9
+numpy<=2.0.2; python_version <= '3.9'
+numpy==2.1.3; python_version > '3.9'
+mypy[faster-cache]>=1.14.0
index f63b590518636f65310b007e4c566007afa883e6..26996a912d53872618e5987c9b5ab71b7a180250 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 This is a setuptools setup-script for the Qt for Python project.
index e629ec570b900b04f8ffb4dbc0394cc87e92c4d6..cc9a7e5b32d037f2e642c25149efe4f9c35c666d 100644 (file)
@@ -29,7 +29,7 @@ else()
             ${CMAKE_CURRENT_SOURCE_DIR}/requirements-android.txt)
 
     set(directories ${CMAKE_CURRENT_SOURCE_DIR}/deploy_lib
-                    ${CMAKE_CURRENT_SOURCE_DIR}/project)
+                    ${CMAKE_CURRENT_SOURCE_DIR}/project_lib)
 
     if(NOT NO_QT_TOOLS STREQUAL "yes")
         set(TOOLS_PATH "${QT6_INSTALL_PREFIX}/${QT6_HOST_INFO_BINDIR}")
@@ -47,7 +47,8 @@ else()
                         "${TOOLS_PATH}/qmlls${CMAKE_EXECUTABLE_SUFFIX}"
                         "${TOOLS_PATH}/qsb${CMAKE_EXECUTABLE_SUFFIX}"
                         "${TOOLS_PATH}/balsam${CMAKE_EXECUTABLE_SUFFIX}"
-                        "${TOOLS_PATH}/balsamui${CMAKE_EXECUTABLE_SUFFIX}")
+                        "${TOOLS_PATH}/balsamui${CMAKE_EXECUTABLE_SUFFIX}"
+                        "${TOOLS_PATH}/svgtoqml${CMAKE_EXECUTABLE_SUFFIX}")
 
         if (APPLE)
             list(APPEND directories "${TOOLS_PATH}/Assistant.app"
@@ -65,6 +66,7 @@ else()
     # pyside6-rcc, pyside6-uic, pyside6-designer, shiboken and pyside6-lupdate entrypoints
     foreach(file ${files})
         if(EXISTS ${file})
+            message(STATUS "Installing tool: ${file}")
             install(FILES "${file}"
                     DESTINATION bin
                     PERMISSIONS
@@ -82,6 +84,18 @@ else()
                 FILE_PERMISSIONS
                 OWNER_EXECUTE OWNER_WRITE OWNER_READ
                 GROUP_EXECUTE GROUP_READ
-                WORLD_EXECUTE WORLD_READ)
+                WORLD_EXECUTE WORLD_READ
+                PATTERN "android_utilities.py" EXCLUDE) # excluding the symlink
     endforeach()
+
+    # dealing with android_utilities.py
+    set(ANDROID_UTILITIES_REALPATH
+        "${CMAKE_CURRENT_SOURCE_DIR}/../../tools/cross_compile_android/android_utilities.py")
+    message(STATUS "Installing ${ANDROID_UTILITIES_REALPATH}")
+    install(FILES "${ANDROID_UTILITIES_REALPATH}"
+            DESTINATION bin/deploy_lib/android
+            PERMISSIONS
+                OWNER_EXECUTE OWNER_WRITE OWNER_READ
+                GROUP_EXECUTE GROUP_READ
+                WORLD_EXECUTE WORLD_READ)
 endif()
index 75269d6228d26d77a340e689cbaf325d0967c87c..46adad61093e12ac5f9f29ee24812ef17c2af7c9 100644 (file)
@@ -1,6 +1,8 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
+import sys
 import argparse
 import logging
 import shutil
@@ -83,13 +85,12 @@ def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = Non
     if config_file_exists:
         logging.info(f"[DEPLOY] Using existing config file {config_file}")
     else:
-        config_file = create_config_file(dry_run=dry_run, config_file=config_file,
-                                         main_file=main_file)
+        config_file = create_config_file(main_file=main_file, dry_run=dry_run)
 
     config = AndroidConfig(config_file=config_file, source_file=main_file,
                            python_exe=python.exe, dry_run=dry_run, android_data=android_data,
                            existing_config_file=config_file_exists,
-                           extra_ignore_dirs=extra_ignore_dirs)
+                           extra_ignore_dirs=extra_ignore_dirs, name=name)
 
     if not config.wheel_pyside and not config.wheel_shiboken:
         raise RuntimeError(f"[DEPLOY] No PySide{MAJOR_VERSION} and Shiboken{MAJOR_VERSION} wheels"
@@ -99,17 +100,13 @@ def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = Non
 
     python.install_dependencies(config=config, packages="android_packages", is_android=True)
 
-    # set application name
-    if name:
-        config.title = name
-
     try:
         config.modules += list(set(extra_modules).difference(set(config.modules)))
 
         # this cannot be done when config file is initialized because cleanup() removes it
         # so this can only be done after the cleanup()
-        config.find_and_set_jars_dir()
-        config.verify_and_set_recipe_dir()
+        config.jars_dir = config.find_jars_dir()
+        config.recipe_dir = config.find_recipe_dir()
 
         # TODO: include qml files from pysidedeploy.spec rather than from extensions
         # buildozer currently includes all the files with .qml extension
@@ -191,8 +188,9 @@ if __name__ == "__main__":
                         required=not config_option_exists())
 
     parser.add_argument("--ndk-path", type=lambda p: Path(p).resolve(),
-                        help=("Path to Android NDK. If omitted, the tool's cache at "
-                              ".pyside6_android_deploy is checked to find the NDK")
+                        help=("Path to Android NDK. The required version is r26b."
+                              "If not provided, the tool will check its cache at "
+                              ".pyside6_android_deploy to find the NDK.")
                         )
 
     parser.add_argument("--sdk-path", type=lambda p: Path(p).resolve(),
@@ -207,6 +205,11 @@ if __name__ == "__main__":
 
     args = parser.parse_args()
 
+    # check if the Python version is greater than 3.12
+    if sys.version_info >= (3, 12):
+        raise RuntimeError("[DEPLOY] Android deployment requires Python version 3.11 or lower. "
+                           "This is due to a restriction in buildozer.")
+
     main(args.name, args.wheel_pyside, args.wheel_shiboken, args.ndk_path, args.sdk_path,
          args.config_file, args.init, args.loglevel, args.dry_run, args.keep_deployment_files,
          args.force, args.extra_ignore_dirs, args.extra_modules)
index 4b6260ccee684379621d5601bbeafa5ebbbde777..d89da4f1b40c5fd1faeac9de0d3efd74529c6845 100644 (file)
@@ -20,9 +20,10 @@ macro(create_and_install_qt_javabindings)
         ${android_main_srcs}/QtService.java
     )
     # set android.jar from the sdk, for compiling the java files into .jar
-    set(sdk_jar_location "${ANDROID_SDK_ROOT}/platforms/${ANDROID_PLATFORM}/android.jar")
+    set(sdk_jar_location "${ANDROID_SDK_ROOT}/platforms/android-${CMAKE_ANDROID_API}/android.jar")
+    file(GLOB sources_list LIST_DIRECTORIES true "${ANDROID_SDK_ROOT}/platforms/android-${CMAKE_ANDROID_API}/*")
     if (NOT EXISTS "${sdk_jar_location}")
-        message(FATAL_ERROR "Could not locate Android SDK jar for api '${api}'")
+        message(FATAL_ERROR "Could not locate Android SDK jar for api '${CMAKE_ANDROID_API}' - ${sdk_jar_location}")
     endif()
 
     # this variable is accessed by qt_internal_add_jar
index aa03d13d07389deb996c9b8136e034db59fbb166..c26c8c627db4c03d4dc73585a6193ccec95b8a3d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """ pyside6-deploy deployment tool
 
@@ -28,6 +29,7 @@
 
 """
 
+import sys
 import argparse
 import logging
 import traceback
@@ -49,12 +51,28 @@ TOOL_DESCRIPTION = dedent(f"""
                           Linux = .bin
                           """)
 
+HELP_MODE = dedent("""
+                   The mode in which the application is deployed. The options are: onefile,
+                   standalone. The default value is onefile.
+
+                   This options translates to the mode Nuitka uses to create the executable.
+
+                   macOS by default uses the --standalone option.
+                   """)
+
 
 def main(main_file: Path = None, name: str = None, config_file: Path = None, init: bool = False,
          loglevel=logging.WARNING, dry_run: bool = False, keep_deployment_files: bool = False,
-         force: bool = False, extra_ignore_dirs: str = None, extra_modules_grouped: str = None):
+         force: bool = False, extra_ignore_dirs: str = None, extra_modules_grouped: str = None,
+         mode: str = None):
 
     logging.basicConfig(level=loglevel)
+
+    # in case pyside6-deploy is run from a completely different location than the project
+    # directory
+    if main_file and main_file.exists():
+        config_file = main_file.parent / "pysidedeploy.spec"
+
     if config_file and not config_file.exists() and not main_file.exists():
         raise RuntimeError(dedent("""
             Directory does not contain main.py file.
@@ -81,21 +99,16 @@ def main(main_file: Path = None, name: str = None, config_file: Path = None, ini
                 extra_modules.append(extra_module)
 
     python = PythonExecutable(dry_run=dry_run, init=init, force=force)
-    config_file_exists = config_file and Path(config_file).exists()
+    config_file_exists = config_file and config_file.exists()
 
     if config_file_exists:
         logging.info(f"[DEPLOY] Using existing config file {config_file}")
     else:
-        config_file = create_config_file(dry_run=dry_run, config_file=config_file,
-                                         main_file=main_file)
+        config_file = create_config_file(main_file=main_file, dry_run=dry_run, )
 
     config = DesktopConfig(config_file=config_file, source_file=main_file, python_exe=python.exe,
                            dry_run=dry_run, existing_config_file=config_file_exists,
-                           extra_ignore_dirs=extra_ignore_dirs)
-
-    # set application name
-    if name:
-        config.title = name
+                           extra_ignore_dirs=extra_ignore_dirs, mode=mode, name=name)
 
     cleanup(config=config)
 
@@ -108,20 +121,28 @@ def main(main_file: Path = None, name: str = None, config_file: Path = None, ini
 
     config.modules += list(set(extra_modules).difference(set(config.modules)))
 
-    # writing config file
-    # in the case of --dry-run, we use default.spec as reference. Do not save the changes
-    # for --dry-run
+    # Do not save the config changes if --dry-run is specified
     if not dry_run:
         config.update_config()
 
     if config.qml_files:
-        logging.info(f"[DEPLOY] Included QML files: {config.qml_files}")
+        logging.info("[DEPLOY] Included QML files: "
+                     f"{[str(qml_file) for qml_file in config.qml_files]}")
 
     if init:
         # config file created above. Exiting.
         logging.info(f"[DEPLOY]: Config file {config.config_file} created")
         return
 
+    # If modules contain QtSql and the platform is macOS, then pyside6-deploy will not work
+    # currently. The fix ideally will have to come from Nuitka.
+    # See PYSIDE-2835
+    # TODO: Remove this check once the issue is fixed in Nuitka
+    # Nuitka Issue: https://github.com/Nuitka/Nuitka/issues/3079
+    if "Sql" in config.modules and sys.platform == "darwin":
+        print("[DEPLOY] QtSql Application is not supported on macOS with pyside6-deploy")
+        return
+
     try:
         # create executable
         if not dry_run:
@@ -135,12 +156,14 @@ def main(main_file: Path = None, name: str = None, config_file: Path = None, ini
                                                excluded_qml_plugins=config.excluded_qml_plugins,
                                                icon=config.icon,
                                                dry_run=dry_run,
-                                               permissions=config.permissions)
+                                               permissions=config.permissions,
+                                               mode=config.mode)
     except Exception:
         print(f"[DEPLOY] Exception occurred: {traceback.format_exc()}")
     finally:
-        if config.generated_files_path and config:
-            finalize(config=config)
+        if config.generated_files_path:
+            if not dry_run:
+                finalize(config=config)
             if not keep_deployment_files:
                 cleanup(config=config)
 
@@ -182,7 +205,11 @@ if __name__ == "__main__":
 
     parser.add_argument("--extra-modules", type=str, help=HELP_EXTRA_MODULES)
 
+    parser.add_argument("--mode", choices=["onefile", "standalone"], default="onefile",
+                        help=HELP_MODE)
+
     args = parser.parse_args()
 
     main(args.main_file, args.name, args.config_file, args.init, args.loglevel, args.dry_run,
-         args.keep_deployment_files, args.force, args.extra_ignore_dirs, args.extra_modules)
+         args.keep_deployment_files, args.force, args.extra_ignore_dirs, args.extra_modules,
+         args.mode)
index a40d0838b4406c0915b022a025688652a20f4d77..3f6190b1d710516cd07bef6a18feaff9f17df991 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 import sys
 from pathlib import Path
 from textwrap import dedent
@@ -17,6 +18,9 @@ else:
     EXE_FORMAT = ".bin"
 
 DEFAULT_APP_ICON = str((Path(__file__).parent / f"pyside_icon{IMAGE_FORMAT}").resolve())
+DEFAULT_IGNORE_DIRS = {"site-packages", "deployment", ".git", ".qtcreator", "build", "dist",
+                       "tests", "doc", "docs", "examples", ".vscode", "__pycache__"}
+
 IMPORT_WARNING_PYSIDE = (f"[DEPLOY] Found 'import PySide6' in file {0}"
                          ". Use 'from PySide6 import <module>' or pass the module"
                          " needed using --extra-modules command line argument")
@@ -40,6 +44,10 @@ HELP_EXTRA_MODULES = dedent("""
                             Example usage 2: --extra-modules=QtNetwork,QtSvg
                             """)
 
+# plugins to be removed from the --include-qt-plugins option because these plugins
+# don't exist in site-package under PySide6/Qt/plugins
+PLUGINS_TO_REMOVE = ["accessiblebridge", "platforms/darwin", "networkaccess", "scenegraph"]
+
 
 def get_all_pyside_modules():
     """
index c3027762c541f314c4dba3bacd791e9599a49917..27d4d7b60a0fd215f63b2996dcb3f0d1b0cee4c1 100644 (file)
@@ -1,5 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+from pathlib import Path
 
 # maps instruction set to Android platform names
 platform_map = {"aarch64": "arm64-v8a",
@@ -10,6 +12,8 @@ platform_map = {"aarch64": "arm64-v8a",
                 "armeabi-v7a": "armeabi-v7a",
                 "x86": "x86"}
 
+ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
+
 from .android_helper import (create_recipe, extract_and_copy_jar, get_wheel_android_arch,
                              AndroidData, get_llvm_readobj, find_lib_dependencies,
                              find_qtlibs_in_wheel)
index ad818c2ff0787739d5d725a1bb828ff291f58be5..6b9386b8ab17988405301b0316c2b22bf557c2a9 100644 (file)
@@ -1,21 +1,22 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 import re
+import sys
 import tempfile
 import logging
 import zipfile
 import xml.etree.ElementTree as ET
 
-from typing import List
 from pathlib import Path
 from pkginfo import Wheel
 
 from . import (extract_and_copy_jar, get_wheel_android_arch, find_lib_dependencies,
-               get_llvm_readobj, find_qtlibs_in_wheel, platform_map, create_recipe)
-from .. import (Config, find_pyside_modules, get_all_pyside_modules, MAJOR_VERSION)
-
-ANDROID_NDK_VERSION = "26b"
-ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
+               get_llvm_readobj, find_qtlibs_in_wheel, platform_map, create_recipe,
+               ANDROID_DEPLOY_CACHE)
+from .. import (Config, get_all_pyside_modules, MAJOR_VERSION)
+from .android_utilities import (ANDROID_NDK_VERSION, ANDROID_NDK_VERSION_NUMBER_SUFFIX,
+                                download_android_ndk)
 
 
 class AndroidConfig(Config):
@@ -24,9 +25,9 @@ class AndroidConfig(Config):
     """
     def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
                  android_data, existing_config_file: bool = False,
-                 extra_ignore_dirs: List[str] = None):
+                 extra_ignore_dirs: list[str] = None, name: str = None):
         super().__init__(config_file=config_file, source_file=source_file, python_exe=python_exe,
-                         dry_run=dry_run, existing_config_file=existing_config_file)
+                         dry_run=dry_run, existing_config_file=existing_config_file, name=name)
 
         self.extra_ignore_dirs = extra_ignore_dirs
 
@@ -50,7 +51,7 @@ class AndroidConfig(Config):
         if android_data.ndk_path:
             # from cli
             self.ndk_path = android_data.ndk_path
-        else:
+        elif not existing_config_file:
             # from config
             ndk_path_temp = self.get_value("buildozer", "ndk_path")
             if ndk_path_temp:
@@ -58,14 +59,21 @@ class AndroidConfig(Config):
             else:
                 ndk_path_temp = (ANDROID_DEPLOY_CACHE / "android-ndk"
                                  / f"android-ndk-r{ANDROID_NDK_VERSION}")
+                if sys.platform == "darwin":
+                    ndk_path_temp = (
+                        ANDROID_DEPLOY_CACHE / "android-ndk"
+                        / f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app/Contents/NDK"
+                    )
                 if ndk_path_temp.exists():
                     self.ndk_path = ndk_path_temp
+                else:
+                    # download NDK
+                    if not ANDROID_DEPLOY_CACHE.exists():
+                        ANDROID_DEPLOY_CACHE.mkdir()
+                        logging.info(f"Cache created at {str(ANDROID_DEPLOY_CACHE.resolve())}")
 
-        if self.ndk_path:
-            print(f"Using Android NDK: {str(self.ndk_path)}")
-        else:
-            raise FileNotFoundError("[DEPLOY] Unable to find Android NDK. Please pass the NDK "
-                                    "path either from the CLI or from pysidedeploy.spec")
+                    logging.info("[DEPLOY] Downloading Android NDK")
+                    self.ndk_path = download_android_ndk(ANDROID_DEPLOY_CACHE)
 
         self.sdk_path = None
         if android_data.sdk_path:
@@ -92,11 +100,11 @@ class AndroidConfig(Config):
         if jars_dir_temp and Path(jars_dir_temp).resolve().exists():
             self.jars_dir = Path(jars_dir_temp).resolve()
 
-        self._arch = None
-        if self.get_value("buildozer", "arch"):
-            self.arch = self.get_value("buildozer", "arch")
+        android_arch = self.get_value("buildozer", "arch")
+        if android_arch:
+            self._arch = android_arch
         else:
-            self._find_and_set_arch()
+            self.arch = self._find_arch()
 
         # maps to correct platform name incase the instruction set was specified
         self._arch = platform_map[self.arch]
@@ -106,31 +114,33 @@ class AndroidConfig(Config):
         self.qt_libs_path: zipfile.Path = find_qtlibs_in_wheel(wheel_pyside=self.wheel_pyside)
         logging.info(f"[DEPLOY] Qt libs path inside wheel: {str(self.qt_libs_path)}")
 
-        if self.get_value("qt", "modules"):
-            self.modules = self.get_value("qt", "modules").split(",")
+        modls = self.get_value("qt", "modules")
+        if modls:
+            self._modules = modls.split(",")
         else:
-            self._find_and_set_pysidemodules()
-            self._find_and_set_qtquick_modules()
-            self.modules += self._find_dependent_qt_modules()
+            modls = self._find_pysidemodules()
+            modls += self._find_qtquick_modules()
+            modls += self._find_dependent_qt_modules(modules=modls)
             # remove duplicates
-            self.modules = list(set(self.modules))
+            self.modules = list(set(modls))
 
         # gets the xml dependency files from Qt installation path
-        self._dependency_files = []
-        self._find_and_set_dependency_files()
+        self.dependency_files = self._find_dependency_files()
 
         dependent_plugins = []
         self._local_libs = []
-        if self.get_value("buildozer", "local_libs"):
-            self._local_libs = self.get_value("buildozer", "local_libs").split(",")
+        loc_libs = self.get_value("buildozer", "local_libs")
+        if loc_libs:
+            self._local_libs = loc_libs.split(",")
         else:
             # the local_libs can also store dependent plugins
             local_libs, dependent_plugins = self._find_local_libs()
             self.local_libs = list(set(local_libs))
 
         self._qt_plugins = []
-        if self.get_value("android", "plugins"):
-            self._qt_plugins = self.get_value("android", "plugins").split(",")
+        qt_plgns = self.get_value("android", "plugins")
+        if qt_plgns:
+            self._qt_plugins = qt_plgns.split(",")
         elif dependent_plugins:
             self._find_plugin_dependencies(dependent_plugins)
             self.qt_plugins = list(set(dependent_plugins))
@@ -256,31 +266,29 @@ class AndroidConfig(Config):
     def dependency_files(self, dependency_files):
         self._dependency_files = dependency_files
 
-    def _find_and_set_pysidemodules(self):
-        self.modules = find_pyside_modules(project_dir=self.project_dir,
-                                           extra_ignore_dirs=self.extra_ignore_dirs,
-                                           project_data=self.project_data)
-        logging.info("The following PySide modules were found from the python files of "
-                     f"the project {self.modules}")
-
-    def find_and_set_jars_dir(self):
+    def find_jars_dir(self):
         """Extract out and copy .jar files to {generated_files_path}
         """
+        jars_dir = None
         if not self.dry_run:
             logging.info("[DEPLOY] Extract and copy jar files from PySide6 wheel to "
                          f"{self.generated_files_path}")
-            self.jars_dir = extract_and_copy_jar(wheel_path=self.wheel_pyside,
-                                                 generated_files_path=self.generated_files_path)
+            jars_dir = extract_and_copy_jar(wheel_path=self.wheel_pyside,
+                                            generated_files_path=self.generated_files_path)
 
-    def _find_and_set_arch(self):
+        return jars_dir
+
+    def _find_arch(self):
         """Find architecture from wheel name
         """
-        self.arch = get_wheel_android_arch(wheel=self.wheel_pyside)
-        if not self.arch:
+        arch = get_wheel_android_arch(wheel=self.wheel_pyside)
+        if not arch:
             raise RuntimeError("[DEPLOY] PySide wheel corrupted. Wheel name should end with"
                                "platform name")
 
-    def _find_dependent_qt_modules(self):
+        return arch
+
+    def _find_dependent_qt_modules(self, modules: list[str]) -> list[str]:
         """
         Given pysidedeploy_config.modules, find all the other dependent Qt modules. This is
         done by using llvm-readobj (readelf) to find the dependent libraries from the module
@@ -302,7 +310,7 @@ class AndroidConfig(Config):
             archive.extractall(tmpdir)
             qt_libs_tmpdir = Path(tmpdir) / lib_path_suffix
             # find the lib folder where Qt libraries are stored
-            for module_name in sorted(self.modules):
+            for module_name in sorted(modules):
                 qt_module_path = qt_libs_tmpdir / f"libQt6{module_name}_{self.arch}.so"
                 if not qt_module_path.exists():
                     raise FileNotFoundError(f"[DEPLOY] libQt6{module_name}_{self.arch}.so not found"
@@ -315,7 +323,7 @@ class AndroidConfig(Config):
             match = lib_pattern.search(dependency)
             if match:
                 module = match.group("mod_name")
-                if module not in self.modules:
+                if module not in modules:
                     dependent_modules.add(module)
 
         # check if the PySide6 binary for the Qt module actually exists
@@ -328,21 +336,23 @@ class AndroidConfig(Config):
 
         return dependent_modules
 
-    def _find_and_set_dependency_files(self) -> List[zipfile.Path]:
+    def _find_dependency_files(self) -> list[zipfile.Path]:
         """
         Based on `modules`, returns the Qt6{module}_{arch}-android-dependencies.xml file, which
         contains the various dependencies of the module, like permissions, plugins etc
         """
         needed_dependency_files = [(f"Qt{MAJOR_VERSION}{module}_{self.arch}"
                                     "-android-dependencies.xml") for module in self.modules]
-
+        found_dependency_files = []
         for dependency_file_name in needed_dependency_files:
             dependency_file = self.qt_libs_path / dependency_file_name
             if dependency_file.exists():
-                self._dependency_files.append(dependency_file)
+                found_dependency_files.append(dependency_file)
 
         logging.info("[DEPLOY] The following dependency files were found: "
-                     f"{*self._dependency_files,}")
+                     f"{*found_dependency_files, }")
+
+        return found_dependency_files
 
     def _find_local_libs(self):
         local_libs = set()
@@ -377,7 +387,7 @@ class AndroidConfig(Config):
 
         return list(local_libs), list(plugins)
 
-    def _find_plugin_dependencies(self, dependent_plugins: List[str]):
+    def _find_plugin_dependencies(self, dependent_plugins: list[str]):
         # The `bundled` element in the dependency xml files points to the folder where
         # additional dependencies for the application exists. Inspecting the depenency files
         # in android, this always points to the specific Qt plugin dependency folder.
@@ -424,12 +434,13 @@ class AndroidConfig(Config):
                             if plugin_infix_name not in dependent_plugins:
                                 dependent_plugins.append(plugin_infix_name)
 
-    def verify_and_set_recipe_dir(self):
+    def find_recipe_dir(self):
         # create recipes
         # https://python-for-android.readthedocs.io/en/latest/recipes/
         # These recipes are manually added through buildozer.spec file to be used by
         # python_for_android while building the distribution
 
+        recipe_dir = None
         if not self.recipes_exist() and not self.dry_run:
             logging.info("[DEPLOY] Creating p4a recipes for PySide6 and shiboken6")
             version = Wheel(self.wheel_pyside).version
@@ -442,5 +453,6 @@ class AndroidConfig(Config):
             create_recipe(version=version, component=f"shiboken{MAJOR_VERSION}",
                           wheel_path=self.wheel_shiboken,
                           generated_files_path=self.generated_files_path)
-            self.recipe_dir = ((self.generated_files_path
-                                / "recipes").resolve())
+            recipe_dir = ((self.generated_files_path
+                           / "recipes").resolve())
+        return recipe_dir
index 7d2f5d57523d556e4b732c83f0ea0fa92348242a..f9398d7ed90aa566b5a5dec9c1d98c810ca6ce70 100644 (file)
@@ -1,11 +1,11 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
+from __future__ import annotations
+import sys
 import logging
 import zipfile
 from dataclasses import dataclass
 from pathlib import Path
-from typing import List, Set
 from zipfile import ZipFile
 
 from jinja2 import Environment, FileSystemLoader
@@ -25,8 +25,8 @@ class AndroidData:
 
 
 def create_recipe(version: str, component: str, wheel_path: str, generated_files_path: Path,
-                  qt_modules: List[str] = None, local_libs: List[str] = None,
-                  plugins: List[str] = None):
+                  qt_modules: list[str] = None, local_libs: list[str] = None,
+                  plugins: list[str] = None):
     '''
     Create python_for_android recipe for PySide6 and shiboken6
     '''
@@ -91,10 +91,10 @@ def get_llvm_readobj(ndk_path: Path) -> Path:
     '''
     # TODO: Requires change if Windows platform supports Android Deployment or if we
     # support host other than linux-x86_64
-    return (ndk_path / "toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readobj")
+    return (ndk_path / f"toolchains/llvm/prebuilt/{sys.platform}-x86_64/bin/llvm-readobj")
 
 
-def find_lib_dependencies(llvm_readobj: Path, lib_path: Path, used_dependencies: Set[str] = None,
+def find_lib_dependencies(llvm_readobj: Path, lib_path: Path, used_dependencies: set[str] = None,
                           dry_run: bool = False):
     """
     Find all the Qt dependencies of a library using llvm_readobj
diff --git a/sources/pyside-tools/deploy_lib/android/android_utilities.py b/sources/pyside-tools/deploy_lib/android/android_utilities.py
new file mode 100644 (file)
index 0000000..45dd874
--- /dev/null
@@ -0,0 +1,311 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import logging
+import shutil
+import re
+import os
+import stat
+import sys
+import subprocess
+
+from urllib import request
+from pathlib import Path
+from packaging import version
+from tqdm import tqdm
+
+# the tag number does not matter much since we update the sdk later
+DEFAULT_SDK_TAG = 6514223
+ANDROID_NDK_VERSION = "26b"
+ANDROID_NDK_VERSION_NUMBER_SUFFIX = "10909125"
+
+
+def run_command(command: list[str], cwd: str | None = None, ignore_fail: bool = False,
+                dry_run: bool = False, accept_prompts: bool = False, show_stdout: bool = False,
+                capture_stdout: bool = False):
+
+    if capture_stdout and not show_stdout:
+        raise RuntimeError("capture_stdout should always be used together with show_stdout")
+
+    if dry_run:
+        print(" ".join(command))
+        return
+
+    input = None
+    if accept_prompts:
+        input = str.encode("y")
+
+    if show_stdout:
+        stdout = None
+    else:
+        stdout = subprocess.DEVNULL
+
+    result = subprocess.run(command, cwd=cwd, input=input, stdout=stdout,
+                            capture_output=capture_stdout)
+
+    if result.returncode != 0 and not ignore_fail:
+        sys.exit(result.returncode)
+
+    if capture_stdout and not result.returncode:
+        return result.stdout.decode("utf-8")
+
+    return None
+
+
+class DownloadProgressBar(tqdm):
+    def update_to(self, b=1, bsize=1, tsize=None):
+        if tsize is not None:
+            self.total = tsize
+        self.update(b * bsize - self.n)
+
+
+class SdkManager:
+    def __init__(self, android_sdk_dir: Path, dry_run: bool = False):
+        self._sdk_manager = android_sdk_dir / "tools" / "bin" / "sdkmanager"
+
+        if not self._sdk_manager.exists():
+            raise RuntimeError(f"Unable to find SdkManager in {str(self._sdk_manager)}")
+
+        if not os.access(self._sdk_manager, os.X_OK):
+            current_permissions = stat.S_IMODE(os.lstat(self._sdk_manager).st_mode)
+            os.chmod(self._sdk_manager, current_permissions | stat.S_IEXEC)
+
+        self._android_sdk_dir = android_sdk_dir
+        self._dry_run = dry_run
+
+    def list_packages(self):
+        command = [self._sdk_manager, f"--sdk_root={self._android_sdk_dir}", "--list"]
+        return run_command(command=command, dry_run=self._dry_run, show_stdout=True,
+                           capture_stdout=True)
+
+    def install(self, *args, accept_license: bool = False, show_stdout=False):
+        command = [str(self._sdk_manager), f"--sdk_root={self._android_sdk_dir}", *args]
+        run_command(command=command, dry_run=self._dry_run,
+                    accept_prompts=accept_license, show_stdout=show_stdout)
+
+
+def extract_zip(file: Path, destination: Path):
+    """
+    Unpacks the zip file into destination preserving all permissions
+
+    TODO: Try to use zipfile module. Currently we cannot use zipfile module here because
+    extractAll() does not preserve permissions.
+
+    In case `unzip` is not available, the user is requested to install it manually
+    """
+    unzip = shutil.which("unzip")
+    if not unzip:
+        raise RuntimeError("Unable to find program unzip. Use `sudo apt-get install unzip`"
+                           "to install it")
+
+    command = [unzip, str(file), "-d", str(destination)]
+    run_command(command=command, show_stdout=True)
+
+
+def extract_dmg(file: Path, destination: Path):
+    output = run_command(['hdiutil', 'attach', '-nobrowse', '-readonly', str(file)],
+                         show_stdout=True, capture_stdout=True)
+
+    # find the mounted volume
+    result = re.search(r'/Volumes/(.*)', output)
+    if not result:
+        raise RuntimeError(f"Unable to find mounted volume for file {file}")
+    mounted_vol_name = result.group(1)
+    if not mounted_vol_name:
+        raise RuntimeError(f"Unable to find mounted volume for file {file}")
+
+    # copy files
+    shutil.copytree(f'/Volumes/{mounted_vol_name}/', destination, dirs_exist_ok=True)
+
+    # Detach mounted volume
+    run_command(['hdiutil', 'detach', f'/Volumes/{mounted_vol_name}'])
+
+
+def _download(url: str, destination: Path):
+    """
+    Download url to destination
+    """
+    headers, download_path = None, None
+    # https://github.com/tqdm/tqdm#hooks-and-callbacks
+    with DownloadProgressBar(unit='B', unit_scale=True, miniters=1, desc=url.split('/')[-1]) as t:
+        download_path, headers = request.urlretrieve(url=url, filename=destination,
+                                                     reporthook=t.update_to)
+    assert Path(download_path).resolve() == destination
+
+
+def download_android_ndk(ndk_path: Path):
+    """
+    Downloads the given ndk_version into ndk_path
+    """
+    ndk_path = ndk_path / "android-ndk"
+    ndk_extension = "dmg" if sys.platform == "darwin" else "zip"
+    ndk_zip_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"
+    ndk_version_path = ""
+    if sys.platform == "linux":
+        ndk_version_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}"
+    elif sys.platform == "darwin":
+        ndk_version_path = (ndk_path
+                            / f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app/Contents/NDK")
+    else:
+        raise RuntimeError(f"Unsupported platform {sys.platform}")
+
+    if ndk_version_path.exists():
+        print(f"NDK path found in {str(ndk_version_path)}")
+    else:
+        try:
+            ndk_path.mkdir(parents=True, exist_ok=True)
+            url = (f"https://dl.google.com/android/repository"
+                   f"/android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}")
+
+            print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
+            _download(url=url, destination=ndk_zip_path)
+
+            print("Unpacking Android Ndk")
+            if sys.platform == "darwin":
+                extract_dmg(file=(ndk_path
+                            / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+                            destination=ndk_path)
+                ndk_version_path = (ndk_version_path
+                                    / (f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app"
+                                       "/Contents/NDK"))
+            else:
+                extract_zip(file=(ndk_path
+                            / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+                            destination=ndk_path)
+        except Exception as e:
+            print(f"Error occurred while downloading and unpacking Android NDK: {e}")
+            if ndk_path.exists():
+                shutil.rmtree(ndk_path)
+            sys.exit(1)
+
+    return ndk_version_path
+
+
+def download_android_commandlinetools(android_sdk_dir: Path):
+    """
+    Downloads Android commandline tools into cltools_path.
+    """
+    sdk_platform = sys.platform if sys.platform != "darwin" else "mac"
+    android_sdk_dir = android_sdk_dir / "android-sdk"
+    url = ("https://dl.google.com/android/repository/"
+           f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
+    cltools_zip_path = (android_sdk_dir
+                        / f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
+    cltools_path = android_sdk_dir / "tools"
+
+    if cltools_path.exists():
+        print(f"Command-line tools found in {str(cltools_path)}")
+    else:
+        try:
+            android_sdk_dir.mkdir(parents=True, exist_ok=True)
+
+            print("Download Android Command Line Tools: "
+                  f"commandlinetools-{sys.platform}-{DEFAULT_SDK_TAG}_latest.zip")
+            _download(url=url, destination=cltools_zip_path)
+
+            print("Unpacking Android Command Line Tools")
+            extract_zip(file=cltools_zip_path, destination=android_sdk_dir)
+        except Exception as e:
+            print(f"Error occurred while downloading and unpacking Android Command Line Tools: {e}")
+            if android_sdk_dir.exists():
+                shutil.rmtree(android_sdk_dir)
+            sys.exit(1)
+
+    return android_sdk_dir
+
+
+def android_list_build_tools_versions(sdk_manager: SdkManager):
+    """
+    List all the build-tools versions available for download
+    """
+    available_packages = sdk_manager.list_packages()
+    build_tools_versions = []
+    lines = available_packages.split('\n')
+
+    for line in lines:
+        if not line.strip().startswith('build-tools;'):
+            continue
+        package_name = line.strip().split(' ')[0]
+        if package_name.count(';') != 1:
+            raise RuntimeError(f"Unable to parse build-tools version: {package_name}")
+        ver = package_name.split(';')[1]
+
+        build_tools_versions.append(version.Version(ver))
+
+    return build_tools_versions
+
+
+def find_installed_buildtools_version(build_tools_dir: Path):
+    """
+    It is possible that the user has multiple build-tools installed. The newer  version is generally
+    used. This function find the newest among the installed build-tools
+    """
+    versions = [version.Version(bt_dir.name) for bt_dir in build_tools_dir.iterdir()
+                if bt_dir.is_dir()]
+    return max(versions)
+
+
+def find_latest_buildtools_version(sdk_manager: SdkManager):
+    """
+    Uses sdk manager to find the latest build-tools version
+    """
+    available_build_tools_v = android_list_build_tools_versions(sdk_manager=sdk_manager)
+
+    if not available_build_tools_v:
+        raise RuntimeError('Unable to find any build tools available for download')
+
+    # find the latest build tools version that is not a release candidate
+    # release candidates end has rc in the version number
+    available_build_tools_v = [v for v in available_build_tools_v if "rc" not in str(v)]
+
+    return max(available_build_tools_v)
+
+
+def install_android_packages(android_sdk_dir: Path, android_api: str, dry_run: bool = False,
+                             accept_license: bool = False, skip_update: bool = False):
+    """
+    Use the sdk manager to install build-tools, platform-tools and platform API
+    """
+    tools_dir = android_sdk_dir / "tools"
+    if not tools_dir.exists():
+        raise RuntimeError("Unable to find Android command-line tools in "
+                           f"{str(tools_dir)}")
+
+    # incase of --verbose flag
+    show_output = (logging.getLogger().getEffectiveLevel() == logging.INFO)
+
+    sdk_manager = SdkManager(android_sdk_dir=android_sdk_dir, dry_run=dry_run)
+
+    # install/upgrade platform-tools
+    if not (android_sdk_dir / "platform-tools").exists():
+        print("Installing/Updating Android platform-tools")
+        sdk_manager.install("platform-tools", accept_license=accept_license,
+                            show_stdout=show_output)
+        # The --update command is only relevant for platform tools
+        if not skip_update:
+            sdk_manager.install("--update", show_stdout=show_output)
+
+    # install/upgrade build-tools
+    buildtools_dir = android_sdk_dir / "build-tools"
+
+    if not buildtools_dir.exists():
+        latest_build_tools_v = find_latest_buildtools_version(sdk_manager=sdk_manager)
+        print(f"Installing Android build-tools version {latest_build_tools_v}")
+        sdk_manager.install(f"build-tools;{latest_build_tools_v}", show_stdout=show_output)
+    else:
+        if not skip_update:
+            latest_build_tools_v = find_latest_buildtools_version(sdk_manager=sdk_manager)
+            installed_build_tools_v = find_installed_buildtools_version(buildtools_dir)
+            if latest_build_tools_v > installed_build_tools_v:
+                print(f"Updating Android build-tools version to {latest_build_tools_v}")
+                sdk_manager.install(f"build-tools;{latest_build_tools_v}", show_stdout=show_output)
+                installed_build_tools_v = latest_build_tools_v
+
+    # install the platform API
+    platform_api_dir = android_sdk_dir / "platforms" / f"android-{android_api}"
+    if not platform_api_dir.exists():
+        print(f"Installing Android platform API {android_api}")
+        sdk_manager.install(f"platforms;android-{android_api}", show_stdout=show_output)
+
+    print("Android packages installation done")
index 0c314c3563a00eb9525cfddc369c393793b61a3b..36d8313e50c42949b2f945ad65255e8eca4babc5 100644 (file)
@@ -1,12 +1,12 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import sys
 import logging
 import xml.etree.ElementTree as ET
 import zipfile
 from pathlib import Path
-from typing import List
 
 from . import AndroidConfig
 from .. import BaseConfig, run_command
@@ -76,7 +76,7 @@ class BuildozerConfig(BaseConfig):
 
         self.update_config()
 
-    def __find_permissions(self, dependency_files: List[zipfile.Path]):
+    def __find_permissions(self, dependency_files: list[zipfile.Path]):
         permissions = set()
         for dependency_file in dependency_files:
             xml_content = dependency_file.read_text()
@@ -85,7 +85,7 @@ class BuildozerConfig(BaseConfig):
                 permissions.add(permission.attrib['name'])
         return permissions
 
-    def __find_jars(self, dependency_files: List[zipfile.Path], jars_dir: Path):
+    def __find_jars(self, dependency_files: list[zipfile.Path], jars_dir: Path):
         jars, init_classes = set(), set()
         for dependency_file in dependency_files:
             xml_content = dependency_file.read_text()
index 8a86157987b7a3e936cb2a03319cf7e0edfc0708..375dadf38155426cae598a366fb9c42d9af9dfb1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import shutil
 import zipfile
index d6ab037bfe445d00b6742879904d00b7b6a79799..0b822d4d1f123fcfb3a45eeaa7cd2da80120ff8b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import shutil
 import zipfile
index 3a7e2a2e28aab8215bfee76b710f8c7b070b7881..03f8c20f8a53245a05453b18f3123bdba7ada6f6 100644 (file)
@@ -1,11 +1,14 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import json
 import subprocess
 import sys
 from pathlib import Path
-from typing import List
+from functools import lru_cache
+from . import DEFAULT_IGNORE_DIRS
+
 
 """
 All utility functions for deployment
@@ -37,24 +40,24 @@ def run_command(command, dry_run: bool, fetch_output: bool = False):
     return command_str, output
 
 
-def run_qmlimportscanner(qml_files: List[Path], dry_run: bool):
+@lru_cache
+def run_qmlimportscanner(project_dir: Path, dry_run: bool):
     """
-        Runs pyside6-qmlimportscanner to find all the imported qml modules
+        Runs pyside6-qmlimportscanner to find all the imported qml modules in project_dir
     """
-    if not qml_files:
-        return []
-
     qml_modules = []
-    cmd = ["pyside6-qmlimportscanner", "-qmlFiles"]
-    cmd.extend([str(qml_file) for qml_file in qml_files])
+    cmd = ["pyside6-qmlimportscanner", "-rootPath", str(project_dir)]
+
+    for ignore_dir in DEFAULT_IGNORE_DIRS:
+        cmd.extend(["-exclude", ignore_dir])
 
     if dry_run:
         run_command(command=cmd, dry_run=True)
 
-    # we need to run qmlimportscanner during dry_run as well to complete the
-    # command being run by nuitka
+    # Run qmlimportscanner during dry_run as well to complete the command being run by nuitka
     _, json_string = run_command(command=cmd, dry_run=False, fetch_output=True)
     json_string = json_string.decode("utf-8")
     json_array = json.loads(json_string)
     qml_modules = [item['name'] for item in json_array if item['type'] == "module"]
+
     return qml_modules
index d59dd92ad353dc06757d51026a98d243e2042ae2..998a175cecefd6d487f89def8cd2fe5e39fa1f67 100644 (file)
@@ -1,17 +1,19 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import sys
 import configparser
 import logging
+import tempfile
 import warnings
 from configparser import ConfigParser
-from typing import List
 from pathlib import Path
+from enum import Enum
 
-from project import ProjectData
-from . import (DEFAULT_APP_ICON, find_pyside_modules, find_permission_categories,
-               QtDependencyReader, run_qmlimportscanner)
+from project_lib import ProjectData, DesignStudioProject
+from . import (DEFAULT_APP_ICON, DEFAULT_IGNORE_DIRS, find_pyside_modules,
+               find_permission_categories, QtDependencyReader, run_qmlimportscanner)
 
 # Some QML plugins like QtCore are excluded from this list as they don't contribute much to
 # executable size. Excluding them saves the extra processing of checking for them in files
@@ -31,6 +33,7 @@ PERMISSION_MAP = {"Bluetooth": "NSBluetoothAlwaysUsageDescription:BluetoothAcces
 class BaseConfig:
     """Wrapper class around any .spec file with function to read and set values for the .spec file
     """
+
     def __init__(self, config_file: Path, comment_prefixes: str = "/",
                  existing_config_file: bool = False) -> None:
         self.config_file = config_file
@@ -40,31 +43,53 @@ class BaseConfig:
         self.parser.read(self.config_file)
 
     def update_config(self):
-        logging.info(f"[DEPLOY] Creating {self.config_file}")
-        with open(self.config_file, "w+") as config_file:
-            self.parser.write(config_file, space_around_delimiters=True)
-
-    def set_value(self, section: str, key: str, new_value: str, raise_warning: bool = True):
+        logging.info(f"[DEPLOY] Updating config file {self.config_file}")
+
+        # This section of code is done to preserve the formatting of the original deploy.spec
+        # file where there is blank line before the comments
+        with tempfile.NamedTemporaryFile('w+', delete=False) as temp_file:
+            self.parser.write(temp_file, space_around_delimiters=True)
+            temp_file_path = temp_file.name
+
+        # Read the temporary file and write back to the original file with blank lines before
+        # comments
+        with open(temp_file_path, 'r') as temp_file, open(self.config_file, 'w') as config_file:
+            previous_line = None
+            for line in temp_file:
+                if (line.lstrip().startswith('#') and previous_line is not None
+                        and not previous_line.lstrip().startswith('#')):
+                    config_file.write('\n')
+                config_file.write(line)
+                previous_line = line
+
+        # Clean up the temporary file
+        Path(temp_file_path).unlink()
+
+    def set_value(self, section: str, key: str, new_value: str, raise_warning: bool = True) -> None:
         try:
             current_value = self.get_value(section, key, ignore_fail=True)
             if current_value != new_value:
                 self.parser.set(section, key, new_value)
         except configparser.NoOptionError:
-            if raise_warning:
-                logging.warning(f"[DEPLOY] Key {key} does not exist")
+            if not raise_warning:
+                return
+            logging.warning(f"[DEPLOY] Set key '{key}': Key does not exist in section '{section}'")
         except configparser.NoSectionError:
-            if raise_warning:
-                logging.warning(f"[DEPLOY] Section {section} does not exist")
+            if not raise_warning:
+                return
+            logging.warning(f"[DEPLOY] Section '{section}' does not exist")
 
-    def get_value(self, section: str, key: str, ignore_fail: bool = False):
+    def get_value(self, section: str, key: str, ignore_fail: bool = False) -> str | None:
         try:
             return self.parser.get(section, key)
         except configparser.NoOptionError:
-            if not ignore_fail:
-                logging.warning(f"[DEPLOY] Key {key} does not exist")
+            if ignore_fail:
+                return None
+            logging.warning(f"[DEPLOY] Get key '{key}': Key does not exist in section {section}")
         except configparser.NoSectionError:
-            if not ignore_fail:
-                logging.warning(f"[DEPLOY] Section {section} does not exist")
+            if ignore_fail:
+                return None
+            logging.warning(f"[DEPLOY] Section '{section}': does not exist")
 
 
 class Config(BaseConfig):
@@ -74,7 +99,8 @@ class Config(BaseConfig):
     """
 
     def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
-                 existing_config_file: bool = False, extra_ignore_dirs: List[str] = None):
+                 existing_config_file: bool = False, extra_ignore_dirs: list[str] = None,
+                 name: str = None):
         super().__init__(config_file=config_file, existing_config_file=existing_config_file)
 
         self.extra_ignore_dirs = extra_ignore_dirs
@@ -94,66 +120,84 @@ class Config(BaseConfig):
             )
         )
 
-        self.title = self.get_value("app", "title")
+        # set application name
+        self.title = self.set_or_fetch(config_property_val=name, config_property_key="title")
 
         # set application icon
         config_icon = self.get_value("app", "icon")
         if config_icon:
-            self.icon = str(Path(config_icon).resolve())
+            self._icon = str(Path(config_icon).resolve())
         else:
             self.icon = DEFAULT_APP_ICON
 
-        self.project_dir = None
-        if self.get_value("app", "project_dir"):
-            self.project_dir = Path(self.get_value("app", "project_dir")).absolute()
-        else:
-            self._find_and_set_project_dir()
-
-        self.exe_dir = None
-        if self.get_value("app", "exec_directory"):
-            self.exe_dir = Path(self.get_value("app", "exec_directory")).absolute()
+        proj_dir = self.get_value("app", "project_dir")
+        if proj_dir:
+            self._project_dir = Path(proj_dir).resolve()
         else:
-            self._find_and_set_exe_dir()
+            self.project_dir = self._find_project_dir()
 
-        self.project_data: ProjectData = None
-        if self.get_value("app", "project_file"):
-            project_file = Path(self.get_value("app", "project_file")).absolute()
-            self.project_data = ProjectData(project_file=project_file)
+        exe_directory = self.get_value("app", "exec_directory")
+        if exe_directory:
+            self._exe_dir = Path(exe_directory).absolute()
         else:
-            self._find_and_set_project_file()
+            self.exe_dir = self._find_exe_dir()
 
-        self.qml_files = []
-        config_qml_files = self.get_value("qt", "qml_files")
-        if config_qml_files and self.project_dir and self.existing_config_file:
-            self.qml_files = [Path(self.project_dir) / file for file in config_qml_files.split(",")]
+        self._project_file = None
+        proj_file = self.get_value("app", "project_file")
+        if proj_file:
+            self._project_file = self.project_dir / proj_file
         else:
-            self._find_and_set_qml_files()
+            proj_file = self._find_project_file()
+            if proj_file:
+                self.project_file = proj_file
+
+        self.project_data = None
+        if self.project_file and self.project_file.exists():
+            self.project_data = ProjectData(project_file=self.project_file)
+
+        self._qml_files = []
+        # Design Studio projects include the qml files using Qt resources
+        if source_file and not DesignStudioProject.is_ds_project(source_file):
+            config_qml_files = self.get_value("qt", "qml_files")
+            if config_qml_files and self.project_dir and self.existing_config_file:
+                self._qml_files = [Path(self.project_dir)
+                                   / file for file in config_qml_files.split(",")]
+            else:
+                self.qml_files = self._find_qml_files()
 
-        self.excluded_qml_plugins = []
-        if self.get_value("qt", "excluded_qml_plugins") and self.existing_config_file:
-            self.excluded_qml_plugins = self.get_value("qt", "excluded_qml_plugins").split(",")
+        self._excluded_qml_plugins = []
+        excl_qml_plugins = self.get_value("qt", "excluded_qml_plugins")
+        if excl_qml_plugins and self.existing_config_file:
+            self._excluded_qml_plugins = excl_qml_plugins.split(",")
         else:
-            self._find_and_set_excluded_qml_plugins()
+            self.excluded_qml_plugins = self._find_excluded_qml_plugins()
 
-        self._generated_files_path = self.project_dir / "deployment"
+        self._generated_files_path = self.source_file.parent / "deployment"
 
         self.modules = []
 
     def set_or_fetch(self, config_property_val, config_property_key, config_property_group="app"):
         """
-        Write to config_file if 'config_property_key' is known without config_file
-        Fetch and return from config_file if 'config_property_key' is unknown, but
-        config_file exists
-        Otherwise, raise an exception
+        Set the configuration value if provided, otherwise fetch the existing value.
+        Raise an exception if neither is available.
+
+        :param value: The value to set if provided.
+        :param key: The configuration key.
+        :param group: The configuration group (default is "app").
+        :return: The configuration value.
+        :raises RuntimeError: If no value is provided and no existing value is found.
         """
+        existing_value = self.get_value(config_property_group, config_property_key)
+
         if config_property_val:
             self.set_value(config_property_group, config_property_key, str(config_property_val))
             return config_property_val
-        elif self.get_value(config_property_group, config_property_key):
-            return self.get_value(config_property_group, config_property_key)
+        elif existing_value:
+            return existing_value
         else:
             raise RuntimeError(
-                f"[DEPLOY] No {config_property_key} specified in config file or as cli option"
+                f"[DEPLOY] No value for {config_property_key} specified in config file or as cli"
+                " option"
             )
 
     @property
@@ -171,6 +215,10 @@ class Config(BaseConfig):
     @qml_files.setter
     def qml_files(self, qml_files):
         self._qml_files = qml_files
+        qml_files = [str(file.absolute().relative_to(self.project_dir.absolute()))
+                     if file.absolute().is_relative_to(self.project_dir) else str(file.absolute())
+                     for file in self.qml_files]
+        self.set_value("qt", "qml_files", ",".join(qml_files))
 
     @property
     def project_dir(self):
@@ -179,6 +227,16 @@ class Config(BaseConfig):
     @project_dir.setter
     def project_dir(self, project_dir):
         self._project_dir = project_dir
+        self.set_value("app", "project_dir", str(project_dir))
+
+    @property
+    def project_file(self):
+        return self._project_file
+
+    @project_file.setter
+    def project_file(self, project_file):
+        self._project_file = project_file
+        self.set_value("app", "project_file", str(project_file.relative_to(self.project_dir)))
 
     @property
     def title(self):
@@ -187,7 +245,6 @@ class Config(BaseConfig):
     @title.setter
     def title(self, title):
         self._title = title
-        self.set_value("app", "title", title)
 
     @property
     def icon(self):
@@ -205,6 +262,7 @@ class Config(BaseConfig):
     @source_file.setter
     def source_file(self, source_file: Path):
         self._source_file = source_file
+        self.set_value("app", "input_file", str(source_file))
 
     @property
     def python_path(self):
@@ -229,6 +287,8 @@ class Config(BaseConfig):
     @excluded_qml_plugins.setter
     def excluded_qml_plugins(self, excluded_qml_plugins):
         self._excluded_qml_plugins = excluded_qml_plugins
+        if excluded_qml_plugins:  # check required for Android
+            self.set_value("qt", "excluded_qml_plugins", ",".join(excluded_qml_plugins))
 
     @property
     def exe_dir(self):
@@ -237,6 +297,7 @@ class Config(BaseConfig):
     @exe_dir.setter
     def exe_dir(self, exe_dir: Path):
         self._exe_dir = exe_dir
+        self.set_value("app", "exec_directory", str(exe_dir))
 
     @property
     def modules(self):
@@ -247,120 +308,92 @@ class Config(BaseConfig):
         self._modules = modules
         self.set_value("qt", "modules", ",".join(modules))
 
-    def _find_and_set_qml_files(self):
-        """Fetches all the qml_files in the folder and sets them if the
-        field qml_files is empty in the config_dir"""
+    def _find_qml_files(self):
+        """
+        Fetches all the qml_files in the folder and sets them if the
+        field qml_files is empty in the config_file
+        """
 
+        qml_files = []
         if self.project_data:
-            qml_files = self.project_data.qml_files
+            qml_files = [(self.project_dir / str(qml_file)) for qml_file in
+                         self.project_data.qml_files]
             for sub_project_file in self.project_data.sub_projects_files:
-                qml_files.extend(ProjectData(project_file=sub_project_file).qml_files)
-            self.qml_files = qml_files
+                qml_files.extend([self.project_dir / str(qml_file) for qml_file in
+                                  ProjectData(project_file=sub_project_file).qml_files])
         else:
-            qml_files_temp = None
-            if self.source_file and self.python_path:
-                if not self.qml_files:
-                    qml_files_temp = list(self.source_file.parent.glob("**/*.qml"))
-
-                # add all QML files, excluding the ones shipped with installed PySide6
-                # The QML files shipped with PySide6 gets added if venv is used,
-                # because of recursive glob
-                if self.python_path.parent.parent == self.source_file.parent:
-                    # python venv path is inside the main source dir
-                    qml_files_temp = list(
-                        set(qml_files_temp) - set(self.python_path.parent.parent.rglob("*.qml"))
-                    )
-
-                if len(qml_files_temp) > 500:
-                    if "site-packages" in str(qml_files_temp[-1]):
-                        raise RuntimeError(
-                            "You are including a lot of QML files from a local virtual env."
-                            " This can lead to errors in deployment."
-                        )
-                    else:
-                        warnings.warn(
-                            "You seem to include a lot of QML files. This can lead to errors in "
-                            "deployment."
-                        )
-
-                if qml_files_temp:
-                    extra_qml_files = [Path(file) for file in qml_files_temp]
-                    self.qml_files.extend(extra_qml_files)
-        if self.qml_files:
-            self.set_value(
-                "qt",
-                "qml_files",
-                ",".join([str(file.absolute().relative_to(self.project_dir))
-                          for file in self.qml_files]),
-            )
-            logging.info("[DEPLOY] QML files identified and set in config_file")
+            # Filter out files from DEFAULT_IGNORE_DIRS
+            qml_files = [
+                file for file in self.project_dir.glob("**/*.qml")
+                if all(part not in file.parts for part in DEFAULT_IGNORE_DIRS)
+            ]
+
+            if len(qml_files) > 500:
+                warnings.warn(
+                    "You seem to include a lot of QML files from "
+                    f"{self.project_dir}. This can lead to errors in deployment."
+                )
+
+        return qml_files
+
+    def _find_project_dir(self) -> Path:
+        if DesignStudioProject.is_ds_project(self.source_file):
+            return DesignStudioProject(self.source_file).project_dir
 
-    def _find_and_set_project_dir(self):
         # there is no other way to find the project_dir than assume it is the parent directory
         # of source_file
-        self.project_dir = self.source_file.parent
+        return self.source_file.parent
 
-        # update input_file path
-        self.set_value("app", "input_file", str(self.source_file.relative_to(self.project_dir)))
+    def _find_project_file(self) -> Path | None:
+        if not self.source_file:
+            raise RuntimeError("[DEPLOY] Source file not set in config file")
 
-        if self.project_dir != Path.cwd():
-            self.set_value("app", "project_dir", str(self.project_dir))
+        if DesignStudioProject.is_ds_project(self.source_file):
+            pyproject_location = self.source_file.parent
         else:
-            self.set_value("app", "project_dir", str(self.project_dir.relative_to(Path.cwd())))
-
-    def _find_and_set_project_file(self):
-        if self.project_dir:
-            files = list(self.project_dir.glob("*.pyproject"))
-        else:
-            logging.exception("[DEPLOY] Project directory not set in config file")
-            raise
+            pyproject_location = self.project_dir
 
+        files = list(pyproject_location.glob("*.pyproject"))
         if not files:
             logging.info("[DEPLOY] No .pyproject file found. Project file not set")
-        elif len(files) > 1:
-            logging.warning("DEPLOY: More that one .pyproject files found. Project file not set")
-            raise
-        else:
-            self.project_data = ProjectData(files[0])
-            self.set_value("app", "project_file", str(files[0].relative_to(self.project_dir)))
-            logging.info(f"[DEPLOY] Project file {files[0]} found and set in config file")
+            return None
+        if len(files) > 1:
+            warnings.warn("DEPLOY: More that one .pyproject files found. Project file not set")
+            return None
 
-    def _find_and_set_excluded_qml_plugins(self):
-        if self.qml_files:
-            self.qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
-                                                        dry_run=self.dry_run))
-            self.excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(self.qml_modules)
+        return files[0]
 
-            # needed for dry_run testing
-            self.excluded_qml_plugins = sorted(self.excluded_qml_plugins)
+    def _find_excluded_qml_plugins(self) -> list[str] | None:
+        if not self.qml_files and not DesignStudioProject.is_ds_project(self.source_file):
+            return None
 
-            if self.excluded_qml_plugins:
-                self.set_value("qt", "excluded_qml_plugins", ",".join(self.excluded_qml_plugins))
+        self.qml_modules = set(run_qmlimportscanner(project_dir=self.project_dir,
+                                                    dry_run=self.dry_run))
+        excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(self.qml_modules)
 
-    def _find_and_set_exe_dir(self):
+        # sorting needed for dry_run testing
+        return sorted(excluded_qml_plugins)
+
+    def _find_exe_dir(self) -> Path:
         if self.project_dir == Path.cwd():
-            self.exe_dir = self.project_dir.relative_to(Path.cwd())
-        else:
-            self.exe_dir = self.project_dir
-        self.exe_dir = Path(
-            self.set_or_fetch(
-                config_property_val=self.exe_dir, config_property_key="exec_directory"
-            )
-        ).absolute()
+            return self.project_dir.relative_to(Path.cwd())
+
+        return self.project_dir
 
-    def _find_and_set_pysidemodules(self):
-        self.modules = find_pyside_modules(project_dir=self.project_dir,
-                                           extra_ignore_dirs=self.extra_ignore_dirs,
-                                           project_data=self.project_data)
+    def _find_pysidemodules(self) -> list[str]:
+        modules = find_pyside_modules(project_dir=self.project_dir,
+                                      extra_ignore_dirs=self.extra_ignore_dirs,
+                                      project_data=self.project_data)
         logging.info("The following PySide modules were found from the Python files of "
-                     f"the project {self.modules}")
+                     f"the project {modules}")
+        return modules
 
-    def _find_and_set_qtquick_modules(self):
+    def _find_qtquick_modules(self) -> list[str]:
         """Identify if QtQuick is used in QML files and add them as dependency
         """
         extra_modules = []
-        if not self.qml_modules:
-            self.qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
+        if not self.qml_modules and self.qml_files:
+            self.qml_modules = set(run_qmlimportscanner(project_dir=self.project_dir,
                                                         dry_run=self.dry_run))
 
         if "QtQuick" in self.qml_modules:
@@ -369,23 +402,32 @@ class Config(BaseConfig):
         if "QtQuick.Controls" in self.qml_modules:
             extra_modules.append("QuickControls2")
 
-        self.modules += extra_modules
+        return extra_modules
 
 
 class DesktopConfig(Config):
     """Wrapper class around pysidedeploy.spec, but specific to Desktop deployment
     """
+
+    class NuitkaMode(Enum):
+        ONEFILE = "onefile"
+        STANDALONE = "standalone"
+
     def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
-                 existing_config_file: bool = False, extra_ignore_dirs: List[str] = None):
+                 existing_config_file: bool = False, extra_ignore_dirs: list[str] = None,
+                 mode: str = "onefile", name: str = None):
         super().__init__(config_file, source_file, python_exe, dry_run, existing_config_file,
-                         extra_ignore_dirs)
+                         extra_ignore_dirs, name=name)
         self.dependency_reader = QtDependencyReader(dry_run=self.dry_run)
-        if self.get_value("qt", "modules"):
-            self.modules = self.get_value("qt", "modules").split(",")
+        modules = self.get_value("qt", "modules")
+        if modules:
+            self._modules = modules.split(",")
         else:
-            self._find_and_set_pysidemodules()
-            self._find_and_set_qtquick_modules()
-            self._find_dependent_qt_modules()
+            modules = self._find_pysidemodules()
+            modules += self._find_qtquick_modules()
+            modules += self._find_dependent_qt_modules(modules=modules)
+            # remove duplicates
+            self.modules = list(set(modules))
 
         self._qt_plugins = []
         if self.get_value("qt", "plugins"):
@@ -400,7 +442,21 @@ class DesktopConfig(Config):
             if nuitka_macos_permissions:
                 self._permissions = nuitka_macos_permissions.split(",")
             else:
-                self._find_and_set_permissions()
+                self.permissions = self._find_permissions()
+
+        self._mode = self.NuitkaMode.ONEFILE
+        if self.get_value("nuitka", "mode") == self.NuitkaMode.STANDALONE.value:
+            self._mode = self.NuitkaMode.STANDALONE
+        elif mode == self.NuitkaMode.STANDALONE.value:
+            self.mode = self.NuitkaMode.STANDALONE
+
+        if DesignStudioProject.is_ds_project(self.source_file):
+            ds_project = DesignStudioProject(self.source_file)
+            if not ds_project.compiled_resources_available():
+                raise RuntimeError(f"[DEPLOY] Compiled resources file not found: "
+                                   f"{ds_project.compiled_resources_file.absolute()}. "
+                                   f"Build the project using 'pyside6-project build' or compile "
+                                   f"the resources manually using pyside6-rcc")
 
     @property
     def qt_plugins(self):
@@ -420,24 +476,33 @@ class DesktopConfig(Config):
         self._permissions = permissions
         self.set_value("nuitka", "macos.permissions", ",".join(permissions))
 
-    def _find_dependent_qt_modules(self):
+    @property
+    def mode(self):
+        return self._mode
+
+    @mode.setter
+    def mode(self, mode: NuitkaMode):
+        self._mode = mode
+        self.set_value("nuitka", "mode", mode.value)
+
+    def _find_dependent_qt_modules(self, modules: list[str]) -> list[str]:
         """
         Given pysidedeploy_config.modules, find all the other dependent Qt modules.
         """
-        all_modules = set(self.modules)
+        all_modules = set(modules)
 
         if not self.dependency_reader.lib_reader:
             warnings.warn(f"[DEPLOY] Unable to find {self.dependency_reader.lib_reader_name}. This "
-                          "tool helps to find the Qt module dependencies of the application. "
-                          "Skipping checking for dependencies.", category=RuntimeWarning)
-            return
+                          f"tool helps to find the Qt module dependencies of the application. "
+                          f"Skipping checking for dependencies.", category=RuntimeWarning)
+            return []
 
-        for module_name in self.modules:
+        for module_name in modules:
             self.dependency_reader.find_dependencies(module=module_name, used_modules=all_modules)
 
-        self.modules = list(all_modules)
+        return list(all_modules)
 
-    def _find_and_set_permissions(self):
+    def _find_permissions(self) -> list[str]:
         """
         Finds and sets the usage description string required for each permission requested by the
         macOS application.
@@ -456,4 +521,4 @@ class DesktopConfig(Config):
             if perm_category in PERMISSION_MAP:
                 permissions.append(PERMISSION_MAP[perm_category])
 
-        self.permissions = permissions
+        return permissions
index 57f99eca13404595eed61c644a0e3e2a21920e29..618a0294d5eebd4ff2e17ef437399404aecae6d8 100644 (file)
@@ -10,7 +10,7 @@ project_dir =
 # Source file path
 input_file =
 
-# Directory where exec is stored
+# Directory where the executable output is generated
 exec_directory =
 
 # Path to .pyproject project file
@@ -25,9 +25,7 @@ icon =
 python_path =
 
 # python packages to install
-# ordered-set: increase compile time performance of nuitka packaging
-# zstandard: provides final executable size optimization
-packages = Nuitka==2.3.2
+packages = Nuitka==2.5.1
 
 # buildozer: for deploying Android application
 android_packages = buildozer==1.5.0,cython==0.29.33
@@ -44,7 +42,8 @@ excluded_qml_plugins =
 # Qt modules used. Comma separated
 modules =
 
-# Qt plugins used by the application
+# Qt plugins used by the application. Only relevant for desktop deployment. For Qt plugins used
+# in Android application see [android][plugins]
 plugins =
 
 [android]
@@ -65,6 +64,9 @@ plugins =
 # eg: NSCameraUsageDescription:CameraAccess
 macos.permissions =
 
+# mode of using Nuitka. Accepts standalone or onefile. Default is onefile.
+mode = onefile
+
 # (str) specify any extra nuitka arguments
 # eg: extra_args = --show-modules --follow-stdlib
 extra_args = --quiet --noinclude-qt-translations
index 2d5b188d35735c2188afb2bea9224d9b2e825303..6bdfb20b7ab821dea910a4e52581ce02a3bf18c3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import ast
 import re
@@ -11,18 +12,17 @@ import logging
 import shutil
 import sys
 from pathlib import Path
-from typing import List, Set
 from functools import lru_cache
 
-from . import IMPORT_WARNING_PYSIDE, run_command
+from . import IMPORT_WARNING_PYSIDE, DEFAULT_IGNORE_DIRS, run_command
 
 
 @lru_cache(maxsize=None)
-def get_py_files(project_dir: Path, extra_ignore_dirs: List[Path] = None, project_data=None):
+def get_py_files(project_dir: Path, extra_ignore_dirs: tuple[Path] = None, project_data=None):
     """Finds and returns all the Python files in the project
     """
     py_candidates = []
-    ignore_dirs = ["__pycache__", "env", "venv", "deployment"]
+    ignore_dirs = DEFAULT_IGNORE_DIRS.copy()
 
     if project_data:
         py_candidates = project_data.python_files
@@ -53,7 +53,7 @@ def get_py_files(project_dir: Path, extra_ignore_dirs: List[Path] = None, projec
     # incase there is not .pyproject file, search all python files in project_dir, except
     # ignore_dirs
     if extra_ignore_dirs:
-        ignore_dirs.extend(extra_ignore_dirs)
+        ignore_dirs.update(extra_ignore_dirs)
 
     # find relevant .py files
     _walk = os.walk(project_dir)
@@ -78,7 +78,7 @@ def get_ast(py_file: Path):
     return tree
 
 
-def find_permission_categories(project_dir: Path, extra_ignore_dirs: List[Path] = None,
+def find_permission_categories(project_dir: Path, extra_ignore_dirs: list[Path] = None,
                                project_data=None):
     """Given the project directory, finds all the permission categories required by the
     project. eg: Camera, Bluetooth, Contacts etc.
@@ -116,6 +116,8 @@ def find_permission_categories(project_dir: Path, extra_ignore_dirs: List[Path]
 
         return set(perm_categories)
 
+    if extra_ignore_dirs is not None:
+        extra_ignore_dirs = tuple(extra_ignore_dirs)
     py_candidates = get_py_files(project_dir, extra_ignore_dirs, project_data)
     for py_candidate in py_candidates:
         all_perm_categories = all_perm_categories.union(pyside_permission_imports(py_candidate))
@@ -126,7 +128,7 @@ def find_permission_categories(project_dir: Path, extra_ignore_dirs: List[Path]
     return all_perm_categories
 
 
-def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None,
+def find_pyside_modules(project_dir: Path, extra_ignore_dirs: list[Path] = None,
                         project_data=None):
     """
     Searches all the python files in the project to find all the PySide modules used by
@@ -135,6 +137,7 @@ def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None,
     all_modules = set()
     mod_pattern = re.compile("PySide6.Qt(?P<mod_name>.*)")
 
+    @lru_cache
     def pyside_module_imports(py_file: Path):
         modules = []
         try:
@@ -142,7 +145,7 @@ def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None,
             for node in ast.walk(tree):
                 if isinstance(node, ast.ImportFrom):
                     main_mod_name = node.module
-                    if main_mod_name.startswith("PySide6"):
+                    if main_mod_name and main_mod_name.startswith("PySide6"):
                         if main_mod_name == "PySide6":
                             # considers 'from PySide6 import QtCore'
                             for imported_module in node.names:
@@ -171,6 +174,8 @@ def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None,
 
         return set(modules)
 
+    if extra_ignore_dirs is not None:
+        extra_ignore_dirs = tuple(extra_ignore_dirs)
     py_candidates = get_py_files(project_dir, extra_ignore_dirs, project_data)
     for py_candidate in py_candidates:
         all_modules = all_modules.union(pyside_module_imports(py_candidate))
@@ -217,12 +222,15 @@ class QtDependencyReader:
         """
         Finds the path to the Qt libs directory inside PySide6 package installation
         """
+        # PYSIDE-2785 consider dist-packages for Debian based systems
         for possible_site_package in site.getsitepackages():
-            if possible_site_package.endswith("site-packages"):
+            if possible_site_package.endswith(("site-packages", "dist-packages")):
                 self.pyside_install_dir = Path(possible_site_package) / "PySide6"
+                if self.pyside_install_dir.exists():
+                    break
 
         if not self.pyside_install_dir:
-            print("Unable to find site-packages. Exiting ...")
+            print("Unable to find where PySide6 is installed. Exiting ...")
             sys.exit(-1)
 
         if sys.platform == "win32":
@@ -234,7 +242,7 @@ class QtDependencyReader:
     def lib_reader(self):
         return self._lib_reader
 
-    def find_dependencies(self, module: str, used_modules: Set[str] = None):
+    def find_dependencies(self, module: str, used_modules: set[str] = None):
         """
         Given a Qt module, find all the other Qt modules it is dependent on and add it to the
         'used_modules' set
@@ -285,7 +293,7 @@ class QtDependencyReader:
         else:
             logging.info(f"[DEPLOY] No Qt dependencies found for {module}")
 
-    def find_plugin_dependencies(self, used_modules: List[str], python_exe: Path) -> List[str]:
+    def find_plugin_dependencies(self, used_modules: list[str], python_exe: Path) -> list[str]:
         """
         Given the modules used by the application, returns all the required plugins
         """
index e8b05e9904e40facb3fd92ab034d42d2d1fb02c2..3786cd20efd744bd50ba20e868c58baf6df08eab 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import logging
 import shutil
@@ -7,7 +8,7 @@ import sys
 from pathlib import Path
 
 from . import EXE_FORMAT
-from .config import Config
+from .config import Config, DesktopConfig
 
 
 def config_option_exists():
@@ -20,58 +21,86 @@ def config_option_exists():
 
 def cleanup(config: Config, is_android: bool = False):
     """
-        Cleanup the generated build folders/files
+    Cleanup the generated build folders/files.
+
+    Parameters:
+    config (Config): The configuration object containing paths and settings.
+    is_android (bool): Flag indicating if the cleanup is for an Android project. Default is False.
     """
     if config.generated_files_path.exists():
-        shutil.rmtree(config.generated_files_path)
-        logging.info("[DEPLOY] Deployment directory purged")
+        try:
+            shutil.rmtree(config.generated_files_path)
+            logging.info("[DEPLOY] Deployment directory purged")
+        except PermissionError as e:
+            print(f"{type(e).__name__}: {e}")
+            logging.warning(f"[DEPLOY] Could not delete {config.generated_files_path}")
 
     if is_android:
         buildozer_spec: Path = config.project_dir / "buildozer.spec"
         if buildozer_spec.exists():
-            buildozer_spec.unlink()
-            logging.info(f"[DEPLOY] {str(buildozer_spec)} removed")
+            try:
+                buildozer_spec.unlink()
+                logging.info(f"[DEPLOY] {str(buildozer_spec)} removed")
+            except PermissionError as e:
+                print(f"{type(e).__name__}: {e}")
+                logging.warning(f"[DEPLOY] Could not delete {buildozer_spec}")
 
         buildozer_build: Path = config.project_dir / ".buildozer"
         if buildozer_build.exists():
-            shutil.rmtree(buildozer_build)
-            logging.info(f"[DEPLOY] {str(buildozer_build)} removed")
+            try:
+                shutil.rmtree(buildozer_build)
+                logging.info(f"[DEPLOY] {str(buildozer_build)} removed")
+            except PermissionError as e:
+                print(f"{type(e).__name__}: {e}")
+                logging.warning(f"[DEPLOY] Could not delete {buildozer_build}")
 
 
-def create_config_file(dry_run: bool = False, config_file: Path = None, main_file: Path = None):
+def create_config_file(main_file: Path, dry_run: bool = False):
     """
-        Sets up a new pysidedeploy.spec or use an existing config file
+        Creates a new pysidedeploy.spec
     """
 
-    if main_file:
-        if main_file.parent != Path.cwd():
-            config_file = main_file.parent / "pysidedeploy.spec"
-        else:
-            config_file = Path.cwd() / "pysidedeploy.spec"
-
+    config_file = main_file.parent / "pysidedeploy.spec"
     logging.info(f"[DEPLOY] Creating config file {config_file}")
-    if not dry_run:
-        shutil.copy(Path(__file__).parent / "default.spec", config_file)
 
+    default_config_file = Path(__file__).parent / "default.spec"
     # the config parser needs a reference to parse. So, in the case of --dry-run
     # use the default.spec file.
     if dry_run:
-        config_file = Path(__file__).parent / "default.spec"
+        return default_config_file
 
+    shutil.copy(default_config_file, config_file)
     return config_file
 
 
-def finalize(config: Config):
+def finalize(config: DesktopConfig):
     """
         Copy the executable into the final location
         For Android deployment, this is done through buildozer
     """
-    generated_exec_path = config.generated_files_path / (config.source_file.stem + EXE_FORMAT)
-    if generated_exec_path.exists() and config.exe_dir:
-        if sys.platform == "darwin":
-            shutil.copytree(generated_exec_path, config.exe_dir / (config.title + EXE_FORMAT),
-                            dirs_exist_ok=True)
-        else:
-            shutil.copy(generated_exec_path, config.exe_dir)
-        print("[DEPLOY] Executed file created in "
-              f"{str(config.exe_dir / (config.source_file.stem + EXE_FORMAT))}")
+    exe_format = EXE_FORMAT
+    if config.mode == DesktopConfig.NuitkaMode.STANDALONE and sys.platform != "darwin":
+        exe_format = ".dist"
+
+    generated_exec_path = config.generated_files_path / (config.source_file.stem + exe_format)
+    if not generated_exec_path.exists():
+        logging.error(f"[DEPLOY] Executable not found at {generated_exec_path.absolute()}")
+        return
+
+    logging.info(f"[DEPLOY] executable generated at {generated_exec_path.absolute()}")
+    if not config.exe_dir:
+        logging.info("[DEPLOY] Not copying output executable because no output directory specified")
+        return
+
+    output_path = config.exe_dir / (config.title + exe_format)
+
+    if sys.platform == "darwin" or config.mode == DesktopConfig.NuitkaMode.STANDALONE:
+        # Copy the folder that contains the executable
+        logging.info(f"[DEPLOY] copying generated folder to {output_path.absolute()}")
+        shutil.copytree(generated_exec_path, output_path, dirs_exist_ok=True)
+    else:
+        # Copy a single file
+        logging.info(f"[DEPLOY] copying generated file to {output_path.absolute()}")
+        shutil.copy(generated_exec_path, output_path)
+
+    print(f"[DEPLOY] Executed file created in {output_path.absolute()}")
index ac9a83f3f1355167bf5dbb5d3055151b4bd4a5b7..bc2896a4c1672cbaaa2ab1afb9a37c88188987e4 100644 (file)
@@ -1,13 +1,19 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
+# enables to use typehints for classes that has not been defined yet or imported
+# used for resolving circular imports
+from __future__ import annotations
 import logging
 import os
+import shlex
 import sys
 from pathlib import Path
-from typing import List
 
-from . import MAJOR_VERSION, run_command
+from project_lib import DesignStudioProject
+from . import MAJOR_VERSION, run_command, DEFAULT_IGNORE_DIRS, PLUGINS_TO_REMOVE
+from .config import DesktopConfig
 
 
 class Nuitka:
@@ -35,11 +41,7 @@ class Nuitka:
                                      "generic"  # plugins that error with Nuitka
                                      ]
 
-        # .webp are considered to be dlls by Nuitka instead of data files causing
-        # the packaging to fail
-        # https://github.com/Nuitka/Nuitka/issues/2854
-        # TODO: Remove .webp when the issue is fixed
-        self.files_to_ignore = [".cpp.o", ".qsb", ".webp"]
+        self.files_to_ignore = [".cpp.o", ".qsb"]
 
     @staticmethod
     def icon_option():
@@ -50,12 +52,43 @@ class Nuitka:
         else:
             return "--macos-app-icon"
 
-    def create_executable(self, source_file: Path, extra_args: str, qml_files: List[Path],
-                          qt_plugins: List[str], excluded_qml_plugins: List[str], icon: str,
-                          dry_run: bool, permissions: List[str]):
+    def _create_windows_command(self, source_file: Path, command: list):
+        """
+        Special case for Windows where the command length is limited to 8191 characters.
+        """
+
+        # if the platform is windows and the command is more than 8191 characters, the command
+        # will fail with the error message "The command line is too long". To avoid this, we will
+        # we will move the source_file to the intermediate source file called deploy_main.py, and
+        # include the Nuitka options direcly in the main file as mentioned in
+        # https://nuitka.net/user-documentation/user-manual.html#nuitka-project-options
+
+        # convert command into a format recognized by Nuitka when written to the main file
+        # the first item is ignore because it is 'python -m nuitka'
+        nuitka_comment_options = []
+        for command_entry in command[4:]:
+            nuitka_comment_options.append(f"# nuitka-project: {command_entry}")
+        nuitka_comment_options_str = "\n".join(nuitka_comment_options)
+        nuitka_comment_options_str += "\n"
+
+        # read the content of the source file
+        new_source_content = (nuitka_comment_options_str
+                              + Path(source_file).read_text(encoding="utf-8"))
+
+        # create and write back the new source content to deploy_main.py
+        new_source_file = source_file.parent / "deploy_main.py"
+        new_source_file.write_text(new_source_content, encoding="utf-8")
+
+        return new_source_file
+
+    def create_executable(self, source_file: Path, extra_args: str, qml_files: list[Path],
+                          qt_plugins: list[str], excluded_qml_plugins: list[str], icon: str,
+                          dry_run: bool, permissions: list[str],
+                          mode: DesktopConfig.NuitkaMode) -> str:
         qt_plugins = [plugin for plugin in qt_plugins if plugin not in self.qt_plugins_to_ignore]
-        extra_args = extra_args.split()
+        extra_args = shlex.split(extra_args)
 
+        # macOS uses the --standalone option by default to create an app bundle
         if sys.platform == "darwin":
             # create an app bundle
             extra_args.extend(["--standalone", "--macos-create-app-bundle"])
@@ -63,18 +96,33 @@ class Nuitka:
             for permission in permissions:
                 extra_args.append(permission_pattern.format(permission=permission))
         else:
-            extra_args.append("--onefile")
+            extra_args.append(f"--{mode.value}")
 
         qml_args = []
         if qml_files:
+            # include all the subdirectories in the project directory as data directories
+            # This includes all the qml modules
+            all_relevant_subdirs = []
+            for subdir in source_file.parent.iterdir():
+                if subdir.is_dir() and subdir.name not in DEFAULT_IGNORE_DIRS:
+                    extra_args.append(f"--include-data-dir={subdir}="
+                                      f"./{subdir.name}")
+                    all_relevant_subdirs.append(subdir)
+
+            # find all the qml files that are not included via the data directories
+            extra_qml_files = [file for file in qml_files
+                               if file.parent not in all_relevant_subdirs]
+
             # This will generate options for each file using:
             #     --include-data-files=ABSOLUTE_PATH_TO_FILE=RELATIVE_PATH_TO ROOT
-            # for each file. This will preserve the directory structure of QML resources.
+            # for each file.
             qml_args.extend(
                 [f"--include-data-files={qml_file.resolve()}="
-                 f"./{qml_file.resolve().relative_to(source_file.parent)}"
-                 for qml_file in qml_files]
+                    f"./{qml_file.resolve().relative_to(source_file.resolve().parent)}"
+                    for qml_file in extra_qml_files]
             )
+
+        if qml_files or DesignStudioProject.is_ds_project(source_file):
             # add qml plugin. The `qml`` plugin name is not present in the module json files shipped
             # with Qt and hence not in `qt_plugins``. However, Nuitka uses the 'qml' plugin name to
             # include the necessary qml plugins. There we have to add it explicitly for a qml
@@ -111,8 +159,26 @@ class Nuitka:
         if qt_plugins:
             # sort qt_plugins so that the result is definitive when testing
             qt_plugins.sort()
+            # remove the following plugins from the qt_plugins list as Nuitka only checks
+            # for plugins within PySide6/Qt/plugins folder, and the following plugins
+            # are not present in the PySide6/Qt/plugins folder
+            qt_plugins = [plugin for plugin in qt_plugins if plugin not in PLUGINS_TO_REMOVE]
             qt_plugins_str = ",".join(qt_plugins)
             command.append(f"--include-qt-plugins={qt_plugins_str}")
 
+        long_command = False
+        if sys.platform == "win32" and len(" ".join(str(cmd) for cmd in command)) > 7000:
+            logging.info("[DEPLOY] Nuitka command too long for Windows. "
+                         "Copying the contents of main Python file to an intermediate "
+                         "deploy_main.py file")
+            long_command = True
+            new_source_file = self._create_windows_command(source_file=source_file, command=command)
+            command = self.nuitka + [os.fspath(new_source_file)]
+
         command_str, _ = run_command(command=command, dry_run=dry_run)
+
+        # if deploy_main.py exists, delete it after the command is run
+        if long_command:
+            os.remove(source_file.parent / "deploy_main.py")
+
         return command_str
index 7cbf323ed89fa9313c2390dc0f18fe56b01ac662..f234f9378f9f7df514ffa922ca78af6dcb5e93c2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import logging
 import os
index 0970f9974f02bf7566bd498ea2902a81f7a07342..d14c3334aa6c7f24983c57f8aa56f9c2f17e53fe 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import ast
 import json
@@ -8,7 +9,7 @@ import sys
 import tokenize
 from argparse import ArgumentParser, RawTextHelpFormatter
 from pathlib import Path
-from typing import Dict, List, Optional, Set, Tuple, Union
+from typing import Union
 
 
 DESCRIPTION = """Parses Python source code to create QObject metatype
@@ -38,32 +39,38 @@ ITEM_MODELS = ["QAbstractListModel", "QAbstractProxyModel",
 QOBJECT_DERIVED = ["QObject", "QQuickItem", "QQuickPaintedItem"] + ITEM_MODELS
 
 
+# Python 3.9 does not support this syntax, yet
+# AstDecorator = ast.Name | ast.Call
+# AstPySideTypeSpec = ast.Name | ast.Constant
 AstDecorator = Union[ast.Name, ast.Call]
 AstPySideTypeSpec = Union[ast.Name, ast.Constant]
 
 
-ClassList = List[dict]
+ClassList = list[dict]
 
 
-PropertyEntry = Dict[str, Union[str, int, bool]]
+# PropertyEntry = dict[str, str | int | bool]
+PropertyEntry = dict[str, Union[str, int, bool]]
 
-Argument = Dict[str, str]
-Arguments = List[Argument]
-Signal = Dict[str, Union[str, Arguments]]
-Slot = Dict[str, Union[str, Arguments]]
+Argument = dict[str, str]
+Arguments = list[Argument]
+# Signal = dict[str, str | Arguments]
+# Slot = dict[str, str | Arguments]
+Signal = dict[str, Union[str, Arguments]]
+Slot = dict[str, Union[str, Arguments]]
 
 
-def _decorator(name: str, value: str) -> Dict[str, str]:
+def _decorator(name: str, value: str) -> dict[str, str]:
     """Create a QML decorator JSON entry"""
     return {"name": name, "value": value}
 
 
-def _attribute(node: ast.Attribute) -> Tuple[str, str]:
+def _attribute(node: ast.Attribute) -> tuple[str, str]:
     """Split an attribute."""
     return node.value.id, node.attr
 
 
-def _name(node: Union[ast.Name, ast.Attribute]) -> str:
+def _name(node: ast.Name | ast.Attribute) -> str:
     """Return the name of something that is either an attribute or a name,
        such as base classes or call.func"""
     if isinstance(node, ast.Attribute):
@@ -82,14 +89,14 @@ def _python_to_cpp_type(type: str) -> str:
     return c if c else type
 
 
-def _parse_property_kwargs(keywords: List[ast.keyword], prop: PropertyEntry):
+def _parse_property_kwargs(keywords: list[ast.keyword], prop: PropertyEntry):
     """Parse keyword arguments of @Property"""
     for k in keywords:
         if k.arg == "notify":
             prop["notify"] = _name(k.value)
 
 
-def _parse_assignment(node: ast.Assign) -> Tuple[Optional[str], Optional[ast.AST]]:
+def _parse_assignment(node: ast.Assign) -> tuple[str | None, ast.AST | None]:
     """Parse an assignment and return a tuple of name, value."""
     if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name):
         var_name = node.targets[0].id
@@ -109,7 +116,7 @@ def _parse_call_args(call: ast.Call):
     """Parse arguments of a Signal call/Slot decorator (type list)."""
     result: Arguments = []
     for n, arg in enumerate(call.args):
-        par_name = f"a{n+1}"
+        par_name = f"a{n + 1}"
         par_type = _parse_pyside_type(arg)
         result.append({"name": par_name, "type": par_type})
     return result
@@ -144,10 +151,10 @@ class MetaObjectDumpVisitor(ast.NodeVisitor):
         self._context = context
         self._json_class_list: ClassList = []
         # Property by name, which will be turned into the JSON List later
-        self._properties: List[PropertyEntry] = []
-        self._signals: List[Signal] = []
+        self._properties: list[PropertyEntry] = []
+        self._signals: list[Signal] = []
         self._within_class: bool = False
-        self._qt_modules: Set[str] = set()
+        self._qt_modules: set[str] = set()
         self._qml_import_name = ""
         self._qml_import_major_version = 0
         self._qml_import_minor_version = 0
@@ -158,7 +165,7 @@ class MetaObjectDumpVisitor(ast.NodeVisitor):
     def qml_import_name(self) -> str:
         return self._qml_import_name
 
-    def qml_import_version(self) -> Tuple[int, int]:
+    def qml_import_version(self) -> tuple[int, int]:
         return (self._qml_import_major_version, self._qml_import_minor_version)
 
     def qt_modules(self):
@@ -215,7 +222,7 @@ class MetaObjectDumpVisitor(ast.NodeVisitor):
         if bases:
             data["superClasses"] = bases
 
-        class_decorators: List[dict] = []
+        class_decorators: list[dict] = []
         for d in node.decorator_list:
             self._parse_class_decorator(d, class_decorators)
 
@@ -247,7 +254,7 @@ class MetaObjectDumpVisitor(ast.NodeVisitor):
                 self._parse_function_decorator(node.name, d)
 
     def _parse_class_decorator(self, node: AstDecorator,
-                               class_decorators: List[dict]):
+                               class_decorators: list[dict]):
         """Parse ClassInfo decorators."""
         if isinstance(node, ast.Call):
             name = _func_name(node)
@@ -298,7 +305,7 @@ class MetaObjectDumpVisitor(ast.NodeVisitor):
         return -1
 
     def _create_property_entry(self, name: str, type: str,
-                               getter: Optional[str] = None) -> PropertyEntry:
+                               getter: str | None = None) -> PropertyEntry:
         """Create a property JSON entry."""
         result: PropertyEntry = {"name": name, "type": type,
                                  "index": len(self._properties)}
@@ -390,7 +397,7 @@ def create_arg_parser(desc: str) -> ArgumentParser:
 
 
 def parse_file(file: Path, context: VisitorContext,
-               suppress_file: bool = False) -> Optional[Dict]:
+               suppress_file: bool = False) -> dict | None:
     """Parse a file and return its json data"""
     ast_tree = MetaObjectDumpVisitor.create_ast(file)
     visitor = MetaObjectDumpVisitor(context)
index 3706a29859300032b1e14f4a8ed6fe5ef164d5f9..1af51e07d10316128eac7c46c10e8ab698a85869 100644 (file)
@@ -1,6 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
+from __future__ import annotations
 
 """
 Builds a '.pyproject' file
@@ -21,16 +21,15 @@ handled per project file.
 """
 import sys
 import os
-from typing import List, Tuple, Optional
 from pathlib import Path
 from argparse import ArgumentParser, RawTextHelpFormatter
 
-from project import (QmlProjectData, check_qml_decorators, is_python_file,
-                     QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX,
-                     SHADER_SUFFIXES, TRANSLATION_SUFFIX,
-                     requires_rebuild, run_command, remove_path,
-                     ProjectData, resolve_project_file, new_project,
-                     ProjectType, ClOptions)
+from project_lib import (QmlProjectData, check_qml_decorators, is_python_file,
+                         QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX,
+                         SHADER_SUFFIXES, TRANSLATION_SUFFIX,
+                         requires_rebuild, run_command, remove_path,
+                         ProjectData, resolve_project_file, new_project,
+                         ProjectType, ClOptions, DesignStudioProject)
 
 MODE_HELP = """build    Builds the project
 run        Builds the project and runs the first file")
@@ -57,7 +56,7 @@ NEW_PROJECT_TYPES = {"new-quick": ProjectType.QUICK,
                      "new-widget": ProjectType.WIDGET}
 
 
-def _sort_sources(files: List[Path]) -> List[Path]:
+def _sort_sources(files: list[Path]) -> list[Path]:
     """Sort the sources for building, ensure .qrc is last since it might depend
        on generated files."""
 
@@ -71,14 +70,15 @@ class Project:
     """
     Class to wrap the various operations on Project
     """
+
     def __init__(self, project_file: Path):
         self.project = ProjectData(project_file=project_file)
         self.cl_options = ClOptions()
 
         # Files for QML modules using the QmlElement decorators
-        self._qml_module_sources: List[Path] = []
-        self._qml_module_dir: Optional[Path] = None
-        self._qml_dir_file: Optional[Path] = None
+        self._qml_module_sources: list[Path] = []
+        self._qml_module_dir: Path | None = None
+        self._qml_dir_file: Path | None = None
         self._qml_project_data = QmlProjectData()
         self._qml_module_check()
 
@@ -113,20 +113,24 @@ class Project:
             print(f"{self.project.project_file.name}, {count} QML file(s),"
                   f" {self._qml_project_data}")
 
-    def _get_artifacts(self, file: Path) -> Tuple[List[Path], Optional[List[str]]]:
+    def _get_artifacts(self, file: Path, output_path: Path | None = None) -> \
+            tuple[list[Path], list[str] | None]:
         """Return path and command for a file's artifact"""
         if file.suffix == ".ui":  # Qt form files
             py_file = f"{file.parent}/ui_{file.stem}.py"
-            return ([Path(py_file)], [UIC_CMD, os.fspath(file), "--rc-prefix", "-o", py_file])
+            return [Path(py_file)], [UIC_CMD, os.fspath(file), "--rc-prefix", "-o", py_file]
         if file.suffix == ".qrc":  # Qt resources
-            py_file = f"{file.parent}/rc_{file.stem}.py"
-            return ([Path(py_file)], [RCC_CMD, os.fspath(file), "-o", py_file])
+            if not output_path:
+                py_file = f"{file.parent}/rc_{file.stem}.py"
+            else:
+                py_file = str(output_path.resolve())
+            return [Path(py_file)], [RCC_CMD, os.fspath(file), "-o", py_file]
         # generate .qmltypes from sources with Qml decorators
         if file.suffix == ".py" and file in self._qml_module_sources:
             assert self._qml_module_dir
             qml_module_dir = os.fspath(self._qml_module_dir)
             json_file = f"{qml_module_dir}/{file.stem}{METATYPES_JSON_SUFFIX}"
-            return ([Path(json_file)], [MOD_CMD, "-o", json_file, os.fspath(file)])
+            return [Path(json_file)], [MOD_CMD, "-o", json_file, os.fspath(file)]
         # Run qmltyperegistrar
         if file.name.endswith(METATYPES_JSON_SUFFIX):
             assert self._qml_module_dir
@@ -137,19 +141,19 @@ class Project:
                    os.fspath(qmltypes_file), "-o", os.fspath(cpp_file),
                    os.fspath(file)]
             cmd.extend(self._qml_project_data.registrar_options())
-            return ([qmltypes_file, cpp_file], cmd)
+            return [qmltypes_file, cpp_file], cmd
 
         if file.name.endswith(TRANSLATION_SUFFIX):
             qm_file = f"{file.parent}/{file.stem}.qm"
             cmd = [LRELEASE_CMD, os.fspath(file), "-qm", qm_file]
-            return ([Path(qm_file)], cmd)
+            return [Path(qm_file)], cmd
 
         if file.suffix in SHADER_SUFFIXES:
             qsb_file = f"{file.parent}/{file.stem}.qsb"
             cmd = [QSB_CMD, "-o", qsb_file, os.fspath(file)]
-            return ([Path(qsb_file)], cmd)
+            return [Path(qsb_file)], cmd
 
-        return ([], None)
+        return [], None
 
     def _regenerate_qmldir(self):
         """Regenerate the 'qmldir' file."""
@@ -161,22 +165,39 @@ class Project:
                 for f in self._qml_module_dir.glob("*.qmltypes"):
                     qf.write(f"typeinfo {f.name}\n")
 
-    def _build_file(self, source: Path):
-        """Build an artifact."""
-        artifacts, command = self._get_artifacts(source)
+    def _build_file(self, source: Path, output_path: Path | None = None):
+        """Build an artifact if necessary."""
+        artifacts, command = self._get_artifacts(source, output_path)
         for artifact in artifacts:
             if self.cl_options.force or requires_rebuild([source], artifact):
                 run_command(command, cwd=self.project.project_file.parent)
             self._build_file(artifact)  # Recurse for QML (json->qmltypes)
 
+    def build_design_studio_resources(self):
+        """
+        The resources that need to be compiled are defined in autogen/settings.py
+        """
+        ds_project = DesignStudioProject(self.project.main_file)
+        if (resources_file_path := ds_project.get_resource_file_path()) is None:
+            return
+
+        compiled_resources_file_path = ds_project.get_compiled_resources_file_path()
+        self._build_file(resources_file_path, compiled_resources_file_path)
+
     def build(self):
-        """Build."""
+        """Build the whole project"""
         for sub_project_file in self.project.sub_projects_files:
             Project(project_file=sub_project_file).build()
+
         if self._qml_module_dir:
             self._qml_module_dir.mkdir(exist_ok=True, parents=True)
+
         for file in _sort_sources(self.project.files):
             self._build_file(file)
+
+        if DesignStudioProject.is_ds_project(self.project.main_file):
+            self.build_design_studio_resources()
+
         self._regenerate_qmldir()
 
     def run(self):
@@ -207,6 +228,9 @@ class Project:
                 first_module_dir = self._qml_module_dir.parts[project_dir_parts]
                 remove_path(self.project.project_file.parent / first_module_dir)
 
+        if DesignStudioProject.is_ds_project(self.project.main_file):
+            DesignStudioProject(self.project.main_file).clean()
+
     def _qmllint(self):
         """Helper for running qmllint on .qml files (non-recursive)."""
         if not self.project.qml_files:
@@ -243,44 +267,31 @@ class Project:
             return
 
         source_files = self.project.python_files + self.project.ui_files
-        cmd_prefix = [LUPDATE_CMD] + [p.name for p in source_files]
+        project_dir = self.project.project_file.parent
+        cmd_prefix = [LUPDATE_CMD] + [os.fspath(p.relative_to(project_dir)) for p in source_files]
         cmd_prefix.append("-ts")
         for ts_file in self.project.ts_files:
             if requires_rebuild(source_files, ts_file):
                 cmd = cmd_prefix
                 cmd.append(ts_file.name)
-                run_command(cmd, cwd=self.project.project_file.parent)
+                run_command(cmd, cwd=project_dir)
 
 
-if __name__ == "__main__":
-    parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
-    parser.add_argument("--quiet", "-q", action="store_true", help="Quiet")
-    parser.add_argument("--dry-run", "-n", action="store_true", help="Only print commands")
-    parser.add_argument("--force", "-f", action="store_true", help="Force rebuild")
-    parser.add_argument("--qml-module", "-Q", action="store_true",
-                        help="Perform check for QML module")
-    mode_choices = ["build", "run", "clean", "qmllint", "deploy", "lupdate"]
-    mode_choices.extend(NEW_PROJECT_TYPES.keys())
-    parser.add_argument("mode", choices=mode_choices, default="build",
-                        type=str, help=MODE_HELP)
-    parser.add_argument("file", help="Project file", nargs="?", type=str)
-
-    options = parser.parse_args()
-    cl_options = ClOptions(dry_run=options.dry_run, quiet=options.quiet, force=options.force,
-                           qml_module=options.qml_module)
-
-    mode = options.mode
+def main(mode: str = None, file: str = None, dry_run: bool = False, quiet: bool = False,
+         force: bool = False, qml_module: bool = None):
+    cl_options = ClOptions(dry_run=dry_run, quiet=quiet,  # noqa: F841
+                           force=force, qml_module=qml_module)
 
     new_project_type = NEW_PROJECT_TYPES.get(mode)
     if new_project_type:
-        if not options.file:
+        if not file:
             print(f"{mode} requires a directory name.", file=sys.stderr)
             sys.exit(1)
-        sys.exit(new_project(options.file, new_project_type))
+        sys.exit(new_project(file, new_project_type))
 
-    project_file = resolve_project_file(options.file)
+    project_file = resolve_project_file(file)
     if not project_file:
-        print(f"Cannot determine project_file {options.file}", file=sys.stderr)
+        print(f"Cannot determine project_file {file}", file=sys.stderr)
         sys.exit(1)
     project = Project(project_file)
     if mode == "build":
@@ -298,3 +309,23 @@ if __name__ == "__main__":
     else:
         print(f"Invalid mode {mode}", file=sys.stderr)
         sys.exit(1)
+
+
+if __name__ == "__main__":
+    parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
+    parser.add_argument("--quiet", "-q", action="store_true", help="Quiet")
+    parser.add_argument("--dry-run", "-n", action="store_true", help="Only print commands")
+    parser.add_argument("--force", "-f", action="store_true", help="Force rebuild")
+    parser.add_argument("--qml-module", "-Q", action="store_true",
+                        help="Perform check for QML module")
+    mode_choices = ["build", "run", "clean", "qmllint", "deploy", "lupdate"]
+    mode_choices.extend(NEW_PROJECT_TYPES.keys())
+    parser.add_argument("mode", choices=mode_choices, default="build",
+                        type=str, help=MODE_HELP)
+
+    # TODO: improve the command structure.
+    #  "File" argument is not correct when doing new-... project
+    parser.add_argument("file", help="Project file", nargs="?", type=str)
+
+    args = parser.parse_args()
+    main(args.mode, args.file, args.dry_run, args.quiet, args.force, args.qml_module)
index 346ef0465ccf1749bfd2f64b649e29e1724a5451..49c7adab2d6ff6dd5e1bf737aff629f49b451199 100644 (file)
@@ -1,4 +1,4 @@
 {
-    "files": ["project.py", "project/__init__.py", "project/newproject.py",
-              "project/project_data.py", "project/utils.py"]
+    "files": ["project.py", "project_lib/__init__.py", "project_lib/design_studio_project.py",
+              "project_lib/newproject.py", "project_lib/project_data.py", "project_lib/utils.py"]
 }
diff --git a/sources/pyside-tools/project/__init__.py b/sources/pyside-tools/project/__init__.py
deleted file mode 100644 (file)
index e57a9ff..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-from dataclasses import dataclass
-
-QTPATHS_CMD = "qtpaths6"
-MOD_CMD = "pyside6-metaobjectdump"
-
-PROJECT_FILE_SUFFIX = ".pyproject"
-QMLDIR_FILE = "qmldir"
-
-QML_IMPORT_NAME = "QML_IMPORT_NAME"
-QML_IMPORT_MAJOR_VERSION = "QML_IMPORT_MAJOR_VERSION"
-QML_IMPORT_MINOR_VERSION = "QML_IMPORT_MINOR_VERSION"
-QT_MODULES = "QT_MODULES"
-
-METATYPES_JSON_SUFFIX = "metatypes.json"
-TRANSLATION_SUFFIX = ".ts"
-SHADER_SUFFIXES = ".vert", ".frag"
-
-
-class Singleton(type):
-    _instances = {}
-
-    def __call__(cls, *args, **kwargs):
-        if cls not in cls._instances:
-            cls._instances[cls] = super().__call__(*args, **kwargs)
-        return cls._instances[cls]
-
-
-@dataclass(frozen=True)
-class ClOptions(metaclass=Singleton):
-    """
-    Dataclass to store the cl options that needs to be passed as arguments.
-    """
-    dry_run: bool
-    quiet: bool
-    force: bool
-    qml_module: bool
-
-
-from .utils import (run_command, requires_rebuild, remove_path, package_dir, qtpaths,
-                    qt_metatype_json_dir, resolve_project_file)
-from .project_data import (is_python_file, ProjectData, QmlProjectData,
-                           check_qml_decorators)
-from .newproject import new_project, ProjectType
diff --git a/sources/pyside-tools/project/newproject.py b/sources/pyside-tools/project/newproject.py
deleted file mode 100644 (file)
index c363a9f..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import json
-import os
-import sys
-from enum import Enum
-from pathlib import Path
-from typing import List, Tuple
-
-"""New project generation code."""
-
-
-Project = List[Tuple[str, str]]  # tuple of (filename, contents).
-
-
-class ProjectType(Enum):
-    WIDGET_FORM = 1
-    WIDGET = 2
-    QUICK = 3
-
-
-_WIDGET_MAIN = """if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    window = MainWindow()
-    window.show()
-    sys.exit(app.exec())
-"""
-
-
-_WIDGET_IMPORTS = """import sys
-from PySide6.QtWidgets import QApplication, QMainWindow
-"""
-
-
-_WIDGET_CLASS_DEFINITION = """class MainWindow(QMainWindow):
-    def __init__(self):
-        super().__init__()
-"""
-
-
-_WIDGET_SETUP_UI_CODE = """        self._ui = Ui_MainWindow()
-        self._ui.setupUi(self)
-"""
-
-
-_MAINWINDOW_FORM = """<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>800</width>
-    <height>600</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>MainWindow</string>
-  </property>
-  <widget class="QWidget" name="centralwidget"/>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>800</width>
-     <height>22</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
- </widget>
-</ui>
-"""
-
-
-_QUICK_FORM = """import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
-    id: window
-    width: 1024
-    height: 600
-    visible: true
-}
-"""
-
-_QUICK_MAIN = """import sys
-from pathlib import Path
-
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtCore import QUrl
-from PySide6.QtQml import QQmlApplicationEngine
-
-
-if __name__ == "__main__":
-    app = QGuiApplication()
-    engine = QQmlApplicationEngine()
-    qml_file = Path(__file__).parent / 'main.qml'
-    engine.load(QUrl.fromLocalFile(qml_file))
-    if not engine.rootObjects():
-        sys.exit(-1)
-    exit_code = app.exec()
-    del engine
-    sys.exit(exit_code)
-"""
-
-
-def _write_project(directory: Path, files: Project):
-    """Write out the project."""
-    file_list = []
-    for file, contents in files:
-        (directory / file).write_text(contents)
-        print(f"Wrote {directory.name}{os.sep}{file}.")
-        file_list.append(file)
-    pyproject = {"files": file_list}
-    pyproject_file = f"{directory}.pyproject"
-    (directory / pyproject_file).write_text(json.dumps(pyproject))
-    print(f"Wrote {directory.name}{os.sep}{pyproject_file}.")
-
-
-def _widget_project() -> Project:
-    """Create a (form-less) widgets project."""
-    main_py = (_WIDGET_IMPORTS + "\n\n" + _WIDGET_CLASS_DEFINITION + "\n\n"
-               + _WIDGET_MAIN)
-    return [("main.py", main_py)]
-
-
-def _ui_form_project() -> Project:
-    """Create a Qt Designer .ui form based widgets project."""
-    main_py = (_WIDGET_IMPORTS
-               + "\nfrom ui_mainwindow import Ui_MainWindow\n\n\n"
-               + _WIDGET_CLASS_DEFINITION + _WIDGET_SETUP_UI_CODE
-               + "\n\n" + _WIDGET_MAIN)
-    return [("main.py", main_py),
-            ("mainwindow.ui", _MAINWINDOW_FORM)]
-
-
-def _qml_project() -> Project:
-    """Create a QML project."""
-    return [("main.py", _QUICK_MAIN),
-            ("main.qml", _QUICK_FORM)]
-
-
-def new_project(directory_s: str,
-                project_type: ProjectType = ProjectType.WIDGET_FORM) -> int:
-    directory = Path(directory_s)
-    if directory.exists():
-        print(f"{directory_s} already exists.", file=sys.stderr)
-        return -1
-    directory.mkdir(parents=True)
-
-    if project_type == ProjectType.WIDGET_FORM:
-        project = _ui_form_project()
-    elif project_type == ProjectType.QUICK:
-        project = _qml_project()
-    else:
-        project = _widget_project()
-    _write_project(directory, project)
-    if project_type == ProjectType.WIDGET_FORM:
-        print(f'Run "pyside6-project build {directory_s}" to build the project')
-    print(f'Run "python {directory.name}{os.sep}main.py" to run the project')
-    return 0
diff --git a/sources/pyside-tools/project/project_data.py b/sources/pyside-tools/project/project_data.py
deleted file mode 100644 (file)
index 52e20be..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import json
-import os
-import subprocess
-import sys
-from typing import List, Tuple
-from pathlib import Path
-from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, TRANSLATION_SUFFIX,
-               qt_metatype_json_dir, MOD_CMD, QML_IMPORT_MAJOR_VERSION,
-               QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME, QT_MODULES)
-
-
-def is_python_file(file: Path) -> bool:
-    return (file.suffix == ".py"
-            or sys.platform == "win32" and file.suffix == ".pyw")
-
-
-class ProjectData:
-    def __init__(self, project_file: Path) -> None:
-        """Parse the project."""
-        self._project_file = project_file
-        self._sub_projects_files: List[Path] = []
-
-        # All sources except subprojects
-        self._files: List[Path] = []
-        # QML files
-        self._qml_files: List[Path] = []
-        # Python files
-        self.main_file: Path = None
-        self._python_files: List[Path] = []
-        # ui files
-        self._ui_files: List[Path] = []
-        # qrc files
-        self._qrc_files: List[Path] = []
-        # ts files
-        self._ts_files: List[Path] = []
-
-        with project_file.open("r") as pyf:
-            pyproject = json.load(pyf)
-            for f in pyproject["files"]:
-                file = Path(project_file.parent / f)
-                if file.suffix == PROJECT_FILE_SUFFIX:
-                    self._sub_projects_files.append(file)
-                else:
-                    self._files.append(file)
-                    if file.suffix == ".qml":
-                        self._qml_files.append(file)
-                    elif is_python_file(file):
-                        if file.stem == "main":
-                            self.main_file = file
-                        self._python_files.append(file)
-                    elif file.suffix == ".ui":
-                        self._ui_files.append(file)
-                    elif file.suffix == ".qrc":
-                        self._qrc_files.append(file)
-                    elif file.suffix == TRANSLATION_SUFFIX:
-                        self._ts_files.append(file)
-
-        if not self.main_file:
-            self._find_main_file()
-
-    @property
-    def project_file(self):
-        return self._project_file
-
-    @property
-    def files(self):
-        return self._files
-
-    @property
-    def main_file(self):
-        return self._main_file
-
-    @main_file.setter
-    def main_file(self, main_file):
-        self._main_file = main_file
-
-    @property
-    def python_files(self):
-        return self._python_files
-
-    @property
-    def ui_files(self):
-        return self._ui_files
-
-    @property
-    def qrc_files(self):
-        return self._qrc_files
-
-    @property
-    def qml_files(self):
-        return self._qml_files
-
-    @property
-    def ts_files(self):
-        return self._ts_files
-
-    @property
-    def sub_projects_files(self):
-        return self._sub_projects_files
-
-    def _find_main_file(self) -> str:
-        """Find the entry point file containing the main function"""
-
-        def is_main(file):
-            return "__main__" in file.read_text(encoding="utf-8")
-
-        if not self.main_file:
-            for python_file in self.python_files:
-                if is_main(python_file):
-                    self.main_file = python_file
-                    return str(python_file)
-
-        # __main__ not found
-        print(
-            "Python file with main function not found. Add the file to" f" {self.project_file}",
-            file=sys.stderr,
-        )
-        sys.exit(1)
-
-
-class QmlProjectData:
-    """QML relevant project data."""
-
-    def __init__(self):
-        self._import_name: str = ""
-        self._import_major_version: int = 0
-        self._import_minor_version: int = 0
-        self._qt_modules: List[str] = []
-
-    def registrar_options(self):
-        result = [
-            "--import-name",
-            self._import_name,
-            "--major-version",
-            str(self._import_major_version),
-            "--minor-version",
-            str(self._import_minor_version),
-        ]
-        if self._qt_modules:
-            # Add Qt modules as foreign types
-            foreign_files: List[str] = []
-            meta_dir = qt_metatype_json_dir()
-            for mod in self._qt_modules:
-                mod_id = mod[2:].lower()
-                pattern = f"qt6{mod_id}_*"
-                if sys.platform != "win32":
-                    pattern += "_"  # qt6core_debug_metatypes.json (Linux)
-                pattern += METATYPES_JSON_SUFFIX
-                for f in meta_dir.glob(pattern):
-                    foreign_files.append(os.fspath(f))
-                    break
-                if foreign_files:
-                    foreign_files_str = ",".join(foreign_files)
-                    result.append(f"--foreign-types={foreign_files_str}")
-        return result
-
-    @property
-    def import_name(self):
-        return self._import_name
-
-    @import_name.setter
-    def import_name(self, n):
-        self._import_name = n
-
-    @property
-    def import_major_version(self):
-        return self._import_major_version
-
-    @import_major_version.setter
-    def import_major_version(self, v):
-        self._import_major_version = v
-
-    @property
-    def import_minor_version(self):
-        return self._import_minor_version
-
-    @import_minor_version.setter
-    def import_minor_version(self, v):
-        self._import_minor_version = v
-
-    @property
-    def qt_modules(self):
-        return self._qt_modules
-
-    @qt_modules.setter
-    def qt_modules(self, v):
-        self._qt_modules = v
-
-    def __str__(self) -> str:
-        vmaj = self._import_major_version
-        vmin = self._import_minor_version
-        return f'"{self._import_name}" v{vmaj}.{vmin}'
-
-    def __bool__(self) -> bool:
-        return len(self._import_name) > 0 and self._import_major_version > 0
-
-
-def _has_qml_decorated_class(class_list: List) -> bool:
-    """Check for QML-decorated classes in the moc json output."""
-    for d in class_list:
-        class_infos = d.get("classInfos")
-        if class_infos:
-            for e in class_infos:
-                if "QML" in e["name"]:
-                    return True
-    return False
-
-
-def check_qml_decorators(py_file: Path) -> Tuple[bool, QmlProjectData]:
-    """Check if a Python file has QML-decorated classes by running a moc check
-    and return whether a class was found and the QML data."""
-    data = None
-    try:
-        cmd = [MOD_CMD, "--quiet", os.fspath(py_file)]
-        with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
-            data = json.load(proc.stdout)
-            proc.wait()
-    except Exception as e:
-        t = type(e).__name__
-        print(f"{t}: running {MOD_CMD} on {py_file}: {e}", file=sys.stderr)
-        sys.exit(1)
-
-    qml_project_data = QmlProjectData()
-    if not data:
-        return (False, qml_project_data)  # No classes in file
-
-    first = data[0]
-    class_list = first["classes"]
-    has_class = _has_qml_decorated_class(class_list)
-    if has_class:
-        v = first.get(QML_IMPORT_NAME)
-        if v:
-            qml_project_data.import_name = v
-        v = first.get(QML_IMPORT_MAJOR_VERSION)
-        if v:
-            qml_project_data.import_major_version = v
-            qml_project_data.import_minor_version = first.get(QML_IMPORT_MINOR_VERSION)
-        v = first.get(QT_MODULES)
-        if v:
-            qml_project_data.qt_modules = v
-    return (has_class, qml_project_data)
diff --git a/sources/pyside-tools/project/utils.py b/sources/pyside-tools/project/utils.py
deleted file mode 100644 (file)
index d2bff65..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import sys
-import subprocess
-from pathlib import Path
-from typing import List, Dict, Optional
-
-from . import QTPATHS_CMD, PROJECT_FILE_SUFFIX, ClOptions
-
-
-def run_command(command: List[str], cwd: str = None, ignore_fail: bool = False):
-    """Run a command observing quiet/dry run"""
-    cloptions = ClOptions()
-    if not cloptions.quiet or cloptions.dry_run:
-        print(" ".join(command))
-    if not cloptions.dry_run:
-        ex = subprocess.call(command, cwd=cwd)
-        if ex != 0 and not ignore_fail:
-            sys.exit(ex)
-
-
-def requires_rebuild(sources: List[Path], artifact: Path) -> bool:
-    """Returns whether artifact needs to be rebuilt depending on sources"""
-    if not artifact.is_file():
-        return True
-    artifact_mod_time = artifact.stat().st_mtime
-    for source in sources:
-        if source.stat().st_mtime > artifact_mod_time:
-            return True
-    return False
-
-
-def _remove_path_recursion(path: Path):
-    """Recursion to remove a file or directory."""
-    if path.is_file():
-        path.unlink()
-    elif path.is_dir():
-        for item in path.iterdir():
-            _remove_path_recursion(item)
-        path.rmdir()
-
-
-def remove_path(path: Path):
-    """Remove path (file or directory) observing opt_dry_run."""
-    cloptions = ClOptions()
-    if not path.exists():
-        return
-    if not cloptions.quiet:
-        print(f"Removing {path.name}...")
-    if cloptions.dry_run:
-        return
-    _remove_path_recursion(path)
-
-
-def package_dir() -> Path:
-    """Return the PySide6 root."""
-    return Path(__file__).resolve().parents[2]
-
-
-_qtpaths_info: Dict[str, str] = {}
-
-
-def qtpaths() -> Dict[str, str]:
-    """Run qtpaths and return a dict of values."""
-    global _qtpaths_info
-    if not _qtpaths_info:
-        output = subprocess.check_output([QTPATHS_CMD, "--query"])
-        for line in output.decode("utf-8").split("\n"):
-            tokens = line.strip().split(":", maxsplit=1)  # "Path=C:\..."
-            if len(tokens) == 2:
-                _qtpaths_info[tokens[0]] = tokens[1]
-    return _qtpaths_info
-
-
-_qt_metatype_json_dir: Optional[Path] = None
-
-
-def qt_metatype_json_dir() -> Path:
-    """Return the location of the Qt QML metatype files."""
-    global _qt_metatype_json_dir
-    if not _qt_metatype_json_dir:
-        qt_dir = package_dir()
-        if sys.platform != "win32":
-            qt_dir /= "Qt"
-        metatypes_dir = qt_dir / "metatypes"
-        if metatypes_dir.is_dir():  # Fully installed case
-            _qt_metatype_json_dir = metatypes_dir
-        else:
-            # Fallback for distro builds/development.
-            print(
-                f"Falling back to {QTPATHS_CMD} to determine metatypes directory.", file=sys.stderr
-            )
-            _qt_metatype_json_dir = Path(qtpaths()["QT_INSTALL_ARCHDATA"]) / "metatypes"
-    return _qt_metatype_json_dir
-
-
-def resolve_project_file(cmdline: str) -> Optional[Path]:
-    """Return the project file from the command  line value, either
-    from the file argument or directory"""
-    project_file = Path(cmdline).resolve() if cmdline else Path.cwd()
-    if project_file.is_file():
-        return project_file
-    if project_file.is_dir():
-        for m in project_file.glob(f"*{PROJECT_FILE_SUFFIX}"):
-            return m
-    return None
diff --git a/sources/pyside-tools/project_lib/__init__.py b/sources/pyside-tools/project_lib/__init__.py
new file mode 100644 (file)
index 0000000..aafaa44
--- /dev/null
@@ -0,0 +1,48 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+QTPATHS_CMD = "qtpaths6"
+MOD_CMD = "pyside6-metaobjectdump"
+
+PROJECT_FILE_SUFFIX = ".pyproject"
+QMLDIR_FILE = "qmldir"
+
+QML_IMPORT_NAME = "QML_IMPORT_NAME"
+QML_IMPORT_MAJOR_VERSION = "QML_IMPORT_MAJOR_VERSION"
+QML_IMPORT_MINOR_VERSION = "QML_IMPORT_MINOR_VERSION"
+QT_MODULES = "QT_MODULES"
+
+METATYPES_JSON_SUFFIX = "metatypes.json"
+TRANSLATION_SUFFIX = ".ts"
+SHADER_SUFFIXES = ".vert", ".frag"
+
+
+class Singleton(type):
+    _instances = {}
+
+    def __call__(cls, *args, **kwargs):
+        if cls not in cls._instances:
+            cls._instances[cls] = super().__call__(*args, **kwargs)
+        return cls._instances[cls]
+
+
+@dataclass(frozen=True)
+class ClOptions(metaclass=Singleton):
+    """
+    Dataclass to store the cl options that needs to be passed as arguments.
+    """
+    dry_run: bool
+    quiet: bool
+    force: bool
+    qml_module: bool
+
+
+from .utils import (run_command, requires_rebuild, remove_path, package_dir, qtpaths,
+                    qt_metatype_json_dir, resolve_project_file)
+from .project_data import (is_python_file, ProjectData, QmlProjectData,
+                           check_qml_decorators)
+from .newproject import new_project, ProjectType
+from .design_studio_project import DesignStudioProject
diff --git a/sources/pyside-tools/project_lib/design_studio_project.py b/sources/pyside-tools/project_lib/design_studio_project.py
new file mode 100644 (file)
index 0000000..6c69e4c
--- /dev/null
@@ -0,0 +1,65 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import logging
+from pathlib import Path
+from typing import Optional
+
+
+class DesignStudioProject:
+    """
+    Class to handle Design Studio projects. The project structure is as follows:
+    - Python folder
+        - autogen folder
+            - settings.py
+            - resources.py (Compiled resources)
+        - main.py
+    <ProjectName>.qrc (Resources collection file)
+    <ProjectName>.qmlproject
+    <ProjectName>.qmlproject.qtds (should be added to .gitignore)
+    ... Other files and folders ...
+    """
+
+    def __init__(self, main_file: Path):
+        self.main_file = main_file
+        self.project_dir = main_file.parent.parent
+        self.compiled_resources_file = self.main_file.parent / "autogen" / "resources.py"
+
+    @staticmethod
+    def is_ds_project(main_file: Path) -> bool:
+        return bool(*main_file.parent.parent.glob("*.qmlproject"))
+
+    def compiled_resources_available(self) -> bool:
+        """
+        Returns whether the resources of the project have been compiled into a .py file.
+        TODO: Make the resources path configurable. Wait for the pyproject TOML configuration
+        """
+        return self.compiled_resources_file.exists()
+
+    def get_resource_file_path(self) -> Optional[Path]:
+        """
+        Return the path to the *.qrc resources file from the project root folder.
+        If not found, log an error message and return None
+        If multiple files are found, log an error message and return None
+        If a single file is found, return its path
+        """
+        resource_files = list(self.project_dir.glob("*.qrc"))
+        if not resource_files:
+            logging.error("No *.qrc resources file found in the project root folder")
+            return None
+        if len(resource_files) > 1:
+            logging.error("Multiple *.qrc resources files found in the project root folder")
+            return None
+        return resource_files[0]
+
+    def get_compiled_resources_file_path(self) -> Path:
+        """
+        Return the path of the output file generated by compiling the *.qrc resources file
+        """
+        # TODO: make this more robust and configurable. Wait for the pyproject TOML configuration
+        return self.main_file.parent / "autogen" / "resources.py"
+
+    def clean(self):
+        """
+        Remove the compiled resources file if it exists
+        """
+        self.compiled_resources_file.unlink(missing_ok=True)
diff --git a/sources/pyside-tools/project_lib/newproject.py b/sources/pyside-tools/project_lib/newproject.py
new file mode 100644 (file)
index 0000000..80ed5a7
--- /dev/null
@@ -0,0 +1,165 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import json
+import os
+import sys
+from enum import Enum
+from pathlib import Path
+
+"""New project generation code."""
+
+
+Project = list[tuple[str, str]]  # tuple of (filename, contents).
+
+
+class ProjectType(Enum):
+    WIDGET_FORM = 1
+    WIDGET = 2
+    QUICK = 3
+
+
+_WIDGET_MAIN = """if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    window = MainWindow()
+    window.show()
+    sys.exit(app.exec())
+"""
+
+
+_WIDGET_IMPORTS = """import sys
+from PySide6.QtWidgets import QApplication, QMainWindow
+"""
+
+
+_WIDGET_CLASS_DEFINITION = """class MainWindow(QMainWindow):
+    def __init__(self):
+        super().__init__()
+"""
+
+
+_WIDGET_SETUP_UI_CODE = """        self._ui = Ui_MainWindow()
+        self._ui.setupUi(self)
+"""
+
+
+_MAINWINDOW_FORM = """<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget"/>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>22</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+</ui>
+"""
+
+
+_QUICK_FORM = """import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+    id: window
+    width: 1024
+    height: 600
+    visible: true
+}
+"""
+
+_QUICK_MAIN = """import sys
+from pathlib import Path
+
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QUrl
+from PySide6.QtQml import QQmlApplicationEngine
+
+
+if __name__ == "__main__":
+    app = QGuiApplication()
+    engine = QQmlApplicationEngine()
+    qml_file = Path(__file__).parent / 'main.qml'
+    engine.load(QUrl.fromLocalFile(qml_file))
+    if not engine.rootObjects():
+        sys.exit(-1)
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
+"""
+
+
+def _write_project(directory: Path, files: Project):
+    """Write out the project."""
+    file_list = []
+    for file, contents in files:
+        (directory / file).write_text(contents)
+        print(f"Wrote {directory.name}{os.sep}{file}.")
+        file_list.append(file)
+    pyproject = {"files": file_list}
+    pyproject_file = f"{directory}.pyproject"
+    (directory / pyproject_file).write_text(json.dumps(pyproject))
+    print(f"Wrote {directory.name}{os.sep}{pyproject_file}.")
+
+
+def _widget_project() -> Project:
+    """Create a (form-less) widgets project."""
+    main_py = (_WIDGET_IMPORTS + "\n\n" + _WIDGET_CLASS_DEFINITION + "\n\n"
+               + _WIDGET_MAIN)
+    return [("main.py", main_py)]
+
+
+def _ui_form_project() -> Project:
+    """Create a Qt Designer .ui form based widgets project."""
+    main_py = (_WIDGET_IMPORTS
+               + "\nfrom ui_mainwindow import Ui_MainWindow\n\n\n"
+               + _WIDGET_CLASS_DEFINITION + _WIDGET_SETUP_UI_CODE
+               + "\n\n" + _WIDGET_MAIN)
+    return [("main.py", main_py),
+            ("mainwindow.ui", _MAINWINDOW_FORM)]
+
+
+def _qml_project() -> Project:
+    """Create a QML project."""
+    return [("main.py", _QUICK_MAIN),
+            ("main.qml", _QUICK_FORM)]
+
+
+def new_project(directory_s: str,
+                project_type: ProjectType = ProjectType.WIDGET_FORM) -> int:
+    directory = Path(directory_s)
+    if directory.exists():
+        print(f"{directory_s} already exists.", file=sys.stderr)
+        return -1
+    directory.mkdir(parents=True)
+
+    if project_type == ProjectType.WIDGET_FORM:
+        project = _ui_form_project()
+    elif project_type == ProjectType.QUICK:
+        project = _qml_project()
+    else:
+        project = _widget_project()
+    _write_project(directory, project)
+    if project_type == ProjectType.WIDGET_FORM:
+        print(f'Run "pyside6-project build {directory_s}" to build the project')
+    print(f'Run "python {directory.name}{os.sep}main.py" to run the project')
+    return 0
diff --git a/sources/pyside-tools/project_lib/project_data.py b/sources/pyside-tools/project_lib/project_data.py
new file mode 100644 (file)
index 0000000..445f02b
--- /dev/null
@@ -0,0 +1,244 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import json
+import os
+import subprocess
+import sys
+from pathlib import Path
+from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, TRANSLATION_SUFFIX,
+               qt_metatype_json_dir, MOD_CMD, QML_IMPORT_MAJOR_VERSION,
+               QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME, QT_MODULES)
+
+
+def is_python_file(file: Path) -> bool:
+    return (file.suffix == ".py"
+            or sys.platform == "win32" and file.suffix == ".pyw")
+
+
+class ProjectData:
+    def __init__(self, project_file: Path) -> None:
+        """Parse the project."""
+        self._project_file = project_file.resolve()
+        self._sub_projects_files: list[Path] = []
+
+        # All sources except subprojects
+        self._files: list[Path] = []
+        # QML files
+        self._qml_files: list[Path] = []
+        # Python files
+        self.main_file: Path = None
+        self._python_files: list[Path] = []
+        # ui files
+        self._ui_files: list[Path] = []
+        # qrc files
+        self._qrc_files: list[Path] = []
+        # ts files
+        self._ts_files: list[Path] = []
+
+        with project_file.open("r") as pyf:
+            pyproject = json.load(pyf)
+            for f in pyproject["files"]:
+                file = Path(project_file.parent / f)
+                if file.suffix == PROJECT_FILE_SUFFIX:
+                    self._sub_projects_files.append(file)
+                else:
+                    self._files.append(file)
+                    if file.suffix == ".qml":
+                        self._qml_files.append(file)
+                    elif is_python_file(file):
+                        if file.stem == "main":
+                            self.main_file = file
+                        self._python_files.append(file)
+                    elif file.suffix == ".ui":
+                        self._ui_files.append(file)
+                    elif file.suffix == ".qrc":
+                        self._qrc_files.append(file)
+                    elif file.suffix == TRANSLATION_SUFFIX:
+                        self._ts_files.append(file)
+
+        if not self.main_file:
+            self._find_main_file()
+
+    @property
+    def project_file(self):
+        return self._project_file
+
+    @property
+    def files(self):
+        return self._files
+
+    @property
+    def main_file(self):
+        return self._main_file
+
+    @main_file.setter
+    def main_file(self, main_file):
+        self._main_file = main_file
+
+    @property
+    def python_files(self):
+        return self._python_files
+
+    @property
+    def ui_files(self):
+        return self._ui_files
+
+    @property
+    def qrc_files(self):
+        return self._qrc_files
+
+    @property
+    def qml_files(self):
+        return self._qml_files
+
+    @property
+    def ts_files(self):
+        return self._ts_files
+
+    @property
+    def sub_projects_files(self):
+        return self._sub_projects_files
+
+    def _find_main_file(self) -> str:
+        """Find the entry point file containing the main function"""
+
+        def is_main(file):
+            return "__main__" in file.read_text(encoding="utf-8")
+
+        if not self.main_file:
+            for python_file in self.python_files:
+                if is_main(python_file):
+                    self.main_file = python_file
+                    return str(python_file)
+
+        # __main__ not found
+        print(
+            f"Python file with main function not found. Add the file to {self.project_file}",
+            file=sys.stderr,
+        )
+        sys.exit(1)
+
+
+class QmlProjectData:
+    """QML relevant project data."""
+
+    def __init__(self):
+        self._import_name: str = ""
+        self._import_major_version: int = 0
+        self._import_minor_version: int = 0
+        self._qt_modules: list[str] = []
+
+    def registrar_options(self):
+        result = [
+            "--import-name",
+            self._import_name,
+            "--major-version",
+            str(self._import_major_version),
+            "--minor-version",
+            str(self._import_minor_version),
+        ]
+        if self._qt_modules:
+            # Add Qt modules as foreign types
+            foreign_files: list[str] = []
+            meta_dir = qt_metatype_json_dir()
+            for mod in self._qt_modules:
+                mod_id = mod[2:].lower()
+                pattern = f"qt6{mod_id}_*"
+                if sys.platform != "win32":
+                    pattern += "_"  # qt6core_debug_metatypes.json (Linux)
+                pattern += METATYPES_JSON_SUFFIX
+                for f in meta_dir.glob(pattern):
+                    foreign_files.append(os.fspath(f))
+                    break
+                if foreign_files:
+                    foreign_files_str = ",".join(foreign_files)
+                    result.append(f"--foreign-types={foreign_files_str}")
+        return result
+
+    @property
+    def import_name(self):
+        return self._import_name
+
+    @import_name.setter
+    def import_name(self, n):
+        self._import_name = n
+
+    @property
+    def import_major_version(self):
+        return self._import_major_version
+
+    @import_major_version.setter
+    def import_major_version(self, v):
+        self._import_major_version = v
+
+    @property
+    def import_minor_version(self):
+        return self._import_minor_version
+
+    @import_minor_version.setter
+    def import_minor_version(self, v):
+        self._import_minor_version = v
+
+    @property
+    def qt_modules(self):
+        return self._qt_modules
+
+    @qt_modules.setter
+    def qt_modules(self, v):
+        self._qt_modules = v
+
+    def __str__(self) -> str:
+        vmaj = self._import_major_version
+        vmin = self._import_minor_version
+        return f'"{self._import_name}" v{vmaj}.{vmin}'
+
+    def __bool__(self) -> bool:
+        return len(self._import_name) > 0 and self._import_major_version > 0
+
+
+def _has_qml_decorated_class(class_list: list) -> bool:
+    """Check for QML-decorated classes in the moc json output."""
+    for d in class_list:
+        class_infos = d.get("classInfos")
+        if class_infos:
+            for e in class_infos:
+                if "QML" in e["name"]:
+                    return True
+    return False
+
+
+def check_qml_decorators(py_file: Path) -> tuple[bool, QmlProjectData]:
+    """Check if a Python file has QML-decorated classes by running a moc check
+    and return whether a class was found and the QML data."""
+    data = None
+    try:
+        cmd = [MOD_CMD, "--quiet", os.fspath(py_file)]
+        with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
+            data = json.load(proc.stdout)
+            proc.wait()
+    except Exception as e:
+        t = type(e).__name__
+        print(f"{t}: running {MOD_CMD} on {py_file}: {e}", file=sys.stderr)
+        sys.exit(1)
+
+    qml_project_data = QmlProjectData()
+    if not data:
+        return (False, qml_project_data)  # No classes in file
+
+    first = data[0]
+    class_list = first["classes"]
+    has_class = _has_qml_decorated_class(class_list)
+    if has_class:
+        v = first.get(QML_IMPORT_NAME)
+        if v:
+            qml_project_data.import_name = v
+        v = first.get(QML_IMPORT_MAJOR_VERSION)
+        if v:
+            qml_project_data.import_major_version = v
+            qml_project_data.import_minor_version = first.get(QML_IMPORT_MINOR_VERSION)
+        v = first.get(QT_MODULES)
+        if v:
+            qml_project_data.qt_modules = v
+    return (has_class, qml_project_data)
diff --git a/sources/pyside-tools/project_lib/utils.py b/sources/pyside-tools/project_lib/utils.py
new file mode 100644 (file)
index 0000000..f1e3f0c
--- /dev/null
@@ -0,0 +1,127 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import sys
+import subprocess
+from pathlib import Path
+import xml.etree.ElementTree as ET
+
+from . import QTPATHS_CMD, PROJECT_FILE_SUFFIX, ClOptions
+
+
+def run_command(command: list[str], cwd: str = None, ignore_fail: bool = False):
+    """Run a command observing quiet/dry run"""
+    cloptions = ClOptions()
+    if not cloptions.quiet or cloptions.dry_run:
+        print(" ".join(command))
+    if not cloptions.dry_run:
+        ex = subprocess.call(command, cwd=cwd)
+        if ex != 0 and not ignore_fail:
+            sys.exit(ex)
+
+
+def qrc_file_requires_rebuild(resources_file_path: Path, compiled_resources_path: Path) -> bool:
+    """Returns whether a compiled qrc file needs to be rebuilt based on the files that references"""
+    root_element = ET.parse(resources_file_path).getroot()
+    project_root = resources_file_path.parent
+
+    files = [project_root / file.text for file in root_element.findall(".//file")]
+
+    compiled_resources_time = compiled_resources_path.stat().st_mtime
+    # If any of the resource files has been modified after the compiled qrc file, the compiled qrc
+    # file needs to be rebuilt
+    if any(file.is_file() and file.stat().st_mtime > compiled_resources_time for file in files):
+        return True
+    return False
+
+
+def requires_rebuild(sources: list[Path], artifact: Path) -> bool:
+    """Returns whether artifact needs to be rebuilt depending on sources"""
+    if not artifact.is_file():
+        return True
+
+    artifact_mod_time = artifact.stat().st_mtime
+    for source in sources:
+        if source.stat().st_mtime > artifact_mod_time:
+            return True
+        # The .qrc file references other files that might have changed
+        if source.suffix == '.qrc' and qrc_file_requires_rebuild(source, artifact):
+            return True
+    return False
+
+
+def _remove_path_recursion(path: Path):
+    """Recursion to remove a file or directory."""
+    if path.is_file():
+        path.unlink()
+    elif path.is_dir():
+        for item in path.iterdir():
+            _remove_path_recursion(item)
+        path.rmdir()
+
+
+def remove_path(path: Path):
+    """Remove path (file or directory) observing opt_dry_run."""
+    cloptions = ClOptions()
+    if not path.exists():
+        return
+    if not cloptions.quiet:
+        print(f"Removing {path.name}...")
+    if cloptions.dry_run:
+        return
+    _remove_path_recursion(path)
+
+
+def package_dir() -> Path:
+    """Return the PySide6 root."""
+    return Path(__file__).resolve().parents[2]
+
+
+_qtpaths_info: dict[str, str] = {}
+
+
+def qtpaths() -> dict[str, str]:
+    """Run qtpaths and return a dict of values."""
+    global _qtpaths_info
+    if not _qtpaths_info:
+        output = subprocess.check_output([QTPATHS_CMD, "--query"])
+        for line in output.decode("utf-8").split("\n"):
+            tokens = line.strip().split(":", maxsplit=1)  # "Path=C:\..."
+            if len(tokens) == 2:
+                _qtpaths_info[tokens[0]] = tokens[1]
+    return _qtpaths_info
+
+
+_qt_metatype_json_dir: Path | None = None
+
+
+def qt_metatype_json_dir() -> Path:
+    """Return the location of the Qt QML metatype files."""
+    global _qt_metatype_json_dir
+    if not _qt_metatype_json_dir:
+        qt_dir = package_dir()
+        if sys.platform != "win32":
+            qt_dir /= "Qt"
+        metatypes_dir = qt_dir / "metatypes"
+        if metatypes_dir.is_dir():  # Fully installed case
+            _qt_metatype_json_dir = metatypes_dir
+        else:
+            # Fallback for distro builds/development.
+            print(
+                f"Falling back to {QTPATHS_CMD} to determine metatypes directory.", file=sys.stderr
+            )
+            _qt_metatype_json_dir = Path(qtpaths()["QT_INSTALL_ARCHDATA"]) / "metatypes"
+    return _qt_metatype_json_dir
+
+
+def resolve_project_file(cmdline: str) -> Path | None:
+    """Return the project file from the command  line value, either
+    from the file argument or directory"""
+    project_file = Path(cmdline).resolve() if cmdline else Path.cwd()
+    if project_file.is_file():
+        return project_file
+    if project_file.is_dir():
+        for m in project_file.glob(f"*{PROJECT_FILE_SUFFIX}"):
+            return m
+    return None
index b369be8a2a7b284dd6d615bcce7093e48b0a1edc..887f2bdda4d127655dbce213e2abae052099ec70 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import importlib
 import os
@@ -214,8 +215,9 @@ def deploy():
 
 
 def android_deploy():
-    if not sys.platform == "linux":
-        print("pyside6-android-deploy only works from a Linux host")
+    if sys.platform == "win32":
+        print("pyside6-android-deploy only works from a Unix host and not a Windows host",
+              file=sys.stderr)
     else:
         android_requirements_file = Path(__file__).parent / "requirements-android.txt"
         with open(android_requirements_file, 'r', encoding='UTF-8') as file:
@@ -239,5 +241,9 @@ def balsamui():
     qt_tool_wrapper("balsamui", sys.argv[1:])
 
 
+def svgtoqml():
+    qt_tool_wrapper("svgtoqml", sys.argv[1:])
+
+
 if __name__ == "__main__":
     main()
index 5d029f93de72677b0a57c174aba35762435dfdee..f138d2e7e49088eaa28e1ef817104b947c46da2e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """pyside6-qml tool implementation. This tool mimics the capabilities of qml runtime utility
 for python and enables quick protyping with python modules"""
@@ -11,7 +12,6 @@ import sys
 import os
 from pathlib import Path
 from pprint import pprint
-from typing import List, Set
 
 from PySide6.QtCore import QCoreApplication, Qt, QLibraryInfo, QUrl, SignalInstance
 from PySide6.QtGui import QGuiApplication, QSurfaceFormat
@@ -20,7 +20,7 @@ from PySide6.QtQuick import QQuickView, QQuickItem
 from PySide6.QtWidgets import QApplication
 
 
-def import_qml_modules(qml_parent_path: Path, module_paths: List[Path] = []):
+def import_qml_modules(qml_parent_path: Path, module_paths: list[Path] = []):
     '''
     Import all the python modules in the qml_parent_path. This way all the classes
     containing the @QmlElement/@QmlNamedElement are also imported
@@ -42,7 +42,7 @@ def import_qml_modules(qml_parent_path: Path, module_paths: List[Path] = []):
             elif module_path.exists() and module_path.suffix == ".py":
                 search_file_paths.append(module_path)
 
-    def import_module(import_module_paths: Set[Path]):
+    def import_module(import_module_paths: set[Path]):
         """Import the modules in 'import_module_paths'"""
         for module_path in import_module_paths:
             module_name = module_path.name[:-3]
@@ -243,4 +243,6 @@ if __name__ == "__main__":
         if args.config == "resizeToItem":
             logging.info("qml: Not a QQuickview item. resizeToItem is done by default")
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index 857b12b676d5e7a033c2e9498718ec23b6244550..bed933cab77f0734d2c4a2361c0dada69dbe6157 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import logging
 import os
index d92fb7589036ec97c8eac7681b0f8cf9f4689844..469b0368a43400cf51388173043b18d99bf07308 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """Tool to dump a Python AST"""
 
index 9a38e803d5874cdbb735cd293fda4410fe301b0f..ad49037173ee9427d90eb5948a3741b1907cee60 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """C++ formatting helper functions and formatter class"""
 
index de62e97008c50a6bd87068c14feab0aa556fab22..c101f1fdfbbb7705809ec237174727ff0a34f61c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """Helper to dump AST nodes for debugging"""
 
index 69bd54aeb9be8410b68234d58c072bac22f4cc73..36fb83b463efc902bc097764c041f62c19c6c632 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """Provides some type information on Qt classes"""
 
@@ -16,38 +17,44 @@ class ClassFlag(Flag):
 
 
 _QT_CLASS_FLAGS = {
-    "QBrush": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
-    "QGradient": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
-    "QIcon": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+    # QtCore
+    "QCoreApplication": ClassFlag.INSTANTIATE_ON_STACK,
+    "QFile": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
+    "QFileInfo": ClassFlag.INSTANTIATE_ON_STACK,
     "QLine": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
     "QLineF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
-    "QPixmap": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+    "QModelIndex": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+    "QPoint": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
     "QPointF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
     "QRect": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
     "QRectF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+    "QSaveFile": ClassFlag.INSTANTIATE_ON_STACK,
+    "QSettings": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
+    "QSize": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
     "QSizeF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
     "QString": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
-    "QFile": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
-    "QSettings": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
     "QTextStream": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
+    # QtGui
+    "QBrush": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
     "QColor": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
-    "QPoint": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
-    "QSize": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
+    "QGradient": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+    "QGuiApplication": ClassFlag.INSTANTIATE_ON_STACK,
+    "QIcon": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+    "QPainter": ClassFlag.INSTANTIATE_ON_STACK,
+    "QPen": ClassFlag.INSTANTIATE_ON_STACK,
+    "QPixmap": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+    # QtWidgets
     "QApplication": ClassFlag.INSTANTIATE_ON_STACK,
     "QColorDialog": ClassFlag.INSTANTIATE_ON_STACK,
-    "QCoreApplication": ClassFlag.INSTANTIATE_ON_STACK,
     "QFileDialog": ClassFlag.INSTANTIATE_ON_STACK,
-    "QFileInfo": ClassFlag.INSTANTIATE_ON_STACK,
     "QFontDialog": ClassFlag.INSTANTIATE_ON_STACK,
-    "QGuiApplication": ClassFlag.INSTANTIATE_ON_STACK,
     "QMessageBox": ClassFlag.INSTANTIATE_ON_STACK,
-    "QPainter": ClassFlag.INSTANTIATE_ON_STACK,
-    "QPen": ClassFlag.INSTANTIATE_ON_STACK,
+    # QtQml
     "QQmlApplicationEngine": ClassFlag.INSTANTIATE_ON_STACK,
     "QQmlComponent": ClassFlag.INSTANTIATE_ON_STACK,
     "QQmlEngine": ClassFlag.INSTANTIATE_ON_STACK,
-    "QQuickView": ClassFlag.INSTANTIATE_ON_STACK,
-    "QSaveFile": ClassFlag.INSTANTIATE_ON_STACK
+    # QtQuick
+    "QQuickView": ClassFlag.INSTANTIATE_ON_STACK
 }
 
 
index 1466ac6b122ecc8a9263c26180e58c8a51bbbbd9..62805cd15eff5a749429a0a2950b369cea6a0a78 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import sys
 
index 894b2a958397218ee822e41b36f6b376bf791826..73dbd2e58660c30bacc97aa41196cb66e5a932e5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import subprocess
 import tempfile
index d5e26c2a821f98044a7c9f0dd17be06ffa1405b5..4f81ba5a25f8b197ad6200534e4d0c4aed6835e5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """Tool to dump Python Tokens"""
 
index 2056951ae87a7b848402ad068f092a7f70ae54ca..e3f39e997cbd4698a510c4ed85880ef595e627fd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """AST visitor printing out C++"""
 
index 1169fd66328975167b6283ea4081e9917013a1fa..9ed5d8427548837afba8ac70391dd005ed5139a8 100644 (file)
@@ -1,2 +1,3 @@
 jinja2
 pkginfo
+tqdm
index 8f6fb6eca201fef48017f2a128cd39b8842f815e..86ca5ff45242d660c0e10ef9924fcc9bed038d77 100644 (file)
@@ -1,5 +1,5 @@
 set(pyside_MAJOR_VERSION "6")
-set(pyside_MINOR_VERSION "7")
-set(pyside_MICRO_VERSION "2")
+set(pyside_MINOR_VERSION "8")
+set(pyside_MICRO_VERSION "2.1")
 set(pyside_PRE_RELEASE_VERSION_TYPE "")
 set(pyside_PRE_RELEASE_VERSION "")
index 61af82d6db51e32fecb8c0cc0e5f16edbe99ed04..c74f6467b3c898a31600f52ce0e4df326e50b08d 100644 (file)
@@ -4,7 +4,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
 
-<typesystem package="PySide6.Qt3DAnimation"
+<typesystem package="PySide6.Qt3DAnimation" doc-package="PySide6.Qt3D"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="Qt3DRender/typesystem_3drender.xml" generate="no"/>
     <namespace-type name="Qt3DAnimation">
@@ -23,7 +23,7 @@
         <object-type name="QAdditiveClipBlend"/>
         <object-type name="QAnimationAspect"/>
         <object-type name="QAnimationCallback">
-            <enum-type name="Flag"/>
+            <enum-type name="Flag" flags="Flags"/>
         </object-type>
         <object-type name="QAnimationClip"/>
         <object-type name="QAnimationClipLoader">
index 985c459b6f9c2cdfb4c8a506d2c1c05983edb18c..c529b637d5a27268919f93ab6c84ef7620de0dd0 100644 (file)
@@ -4,7 +4,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
 
-<typesystem package="PySide6.Qt3DCore"
+<typesystem package="PySide6.Qt3DCore" doc-package="PySide6.Qt3D" doc-mode="flat"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
     <smart-pointer-type name="QSharedPointer" type="shared" getter="data"
index d281ae15f36107622573db6bcfe5f5fc3f8b10be..b19615bf3abdfa21acfbe4ae19b5977ab01be041 100644 (file)
@@ -4,7 +4,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
 
-<typesystem package="PySide6.Qt3DExtras"
+<typesystem package="PySide6.Qt3DExtras" doc-package="PySide6.Qt3D" doc-mode="flat"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="Qt3DRender/typesystem_3drender.xml" generate="no"/>
     <namespace-type name="Qt3DExtras">
index fbdfc596148fda376dd48915783f5b207d1cb732..83fb09b8f21661b00e6c96d06f1c9c4b94b72635 100644 (file)
@@ -4,7 +4,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
 
-<typesystem package="PySide6.Qt3DInput"
+<typesystem package="PySide6.Qt3DInput" doc-package="PySide6.Qt3D" doc-mode="flat"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
     <namespace-type name="Qt3DInput">
index 60f36253407896ebef97f49f9b0e8be0ce5686a6..dd9ad2aea57e347b590c8654b2c883f4e8325487 100644 (file)
@@ -4,7 +4,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
 
-<typesystem package="PySide6.Qt3DLogic"
+<typesystem package="PySide6.Qt3DLogic" doc-package="PySide6.Qt3D" doc-mode="flat"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
     <namespace-type name="Qt3DLogic">
index 66fd709271929664bf7ddde1030afca09b8b6c10..671e1fd9bf2a3f696b4320d2ef3b5df1a3bc37da 100644 (file)
@@ -4,7 +4,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
 
-<typesystem package="PySide6.Qt3DRender"
+<typesystem package="PySide6.Qt3DRender" doc-package="PySide6.Qt3D" doc-mode="flat"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
     <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
index 60d1846d1912255fee047ce5966fe2fb25b7b01e..d284e4f6a5e9c2b4bf3fa6929f90d46180ea9226 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from .events import (
     QAsyncioEventLoopPolicy, QAsyncioEventLoop, QAsyncioHandle, QAsyncioTimerHandle
@@ -7,8 +8,9 @@ from .events import (
 from .futures import QAsyncioFuture
 from .tasks import QAsyncioTask
 
+from typing import Coroutine, Any
+
 import asyncio
-import typing
 
 __all__ = [
     "QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
@@ -17,12 +19,33 @@ __all__ = [
 ]
 
 
-def run(coro: typing.Optional[typing.Coroutine] = None,
-        keep_running: bool = True,
-        quit_qapp: bool = True, *,
-        handle_sigint: bool = False,
-        debug: typing.Optional[bool] = None) -> typing.Any:
-    """Run the QtAsyncio event loop."""
+def run(coro: Coroutine | None = None,
+        keep_running: bool = True, quit_qapp: bool = True, *, handle_sigint: bool = False,
+        debug: bool | None = None) -> Any:
+    """
+    Run the QtAsyncio event loop.
+
+    If there is no instance of a QCoreApplication, QGuiApplication or
+    QApplication yet, a new instance of QCoreApplication is created.
+
+    :param coro:            The coroutine to run. Optional if keep_running is
+                            True.
+    :param keep_running:    If True, QtAsyncio (the asyncio event loop) will
+                            continue running after the coroutine finished, or
+                            run "forever" if no coroutine was provided.
+                            If False, QtAsyncio will stop after the
+                            coroutine finished. A coroutine must be provided if
+                            this argument is set to False.
+    :param quit_qapp:       If True, the QCoreApplication will quit when
+                            QtAsyncio (the asyncio event loop) stops.
+                            If False, the QCoreApplication will remain active
+                            after QtAsyncio stops, and can continue to be used.
+    :param handle_sigint:   If True, the SIGINT signal will be handled by the
+                            event loop, causing it to stop.
+    :param debug:           If True, the event loop will run in debug mode.
+                            If False, the event loop will run in normal mode.
+                            If None, the default behavior is used.
+    """
 
     # Event loop policies are expected to be deprecated with Python 3.13, with
     # subsequent removal in Python 3.15. At that point, part of the current
@@ -49,7 +72,7 @@ def run(coro: typing.Optional[typing.Coroutine] = None,
             ret = asyncio.run(coro, debug=debug)
         else:
             exc = RuntimeError(
-                "QtAsyncio was set to keep running after the coroutine "
+                "QtAsyncio was set not to keep running after the coroutine "
                 "finished, but no coroutine was provided.")
 
     asyncio.set_event_loop_policy(default_policy)
index 86b89014a94e9796d779c976e7076828af73217c..36c7fea979094c80bf511687838338895f903feb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from PySide6.QtCore import (QCoreApplication, QDateTime, QDeadlineTimer,
                             QEventLoop, QObject, QTimer, QThread, Slot)
@@ -7,6 +8,8 @@ from PySide6.QtCore import (QCoreApplication, QDateTime, QDeadlineTimer,
 from . import futures
 from . import tasks
 
+from typing import Any, Callable
+
 import asyncio
 import collections.abc
 import concurrent.futures
@@ -16,7 +19,6 @@ import os
 import signal
 import socket
 import subprocess
-import typing
 import warnings
 
 __all__ = [
@@ -40,7 +42,7 @@ class QAsyncioExecutorWrapper(QObject):
     the actual callable for the executor into this new event loop.
     """
 
-    def __init__(self, func: typing.Callable, *args: typing.Tuple) -> None:
+    def __init__(self, func: Callable, *args: tuple) -> None:
         super().__init__()
         self._loop: QEventLoop
         self._func = func
@@ -63,7 +65,10 @@ class QAsyncioExecutorWrapper(QObject):
         self._loop = QEventLoop()
         asyncio.events._set_running_loop(self._loop)
 
-        QTimer.singleShot(0, self._loop, lambda: self._cb())
+        # The do() function will always be executed from the new executor
+        # thread and never from outside, so using the overload without the
+        # context argument is sufficient.
+        QTimer.singleShot(0, lambda: self._cb())
 
         self._loop.exec()
         if self._exception is not None:
@@ -87,16 +92,10 @@ class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
     https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553
     """
     def __init__(self,
-                 application: typing.Optional[QCoreApplication] = None,
                  quit_qapp: bool = True,
                  handle_sigint: bool = False) -> None:
         super().__init__()
-        if application is None:
-            if QCoreApplication.instance() is None:
-                application = QCoreApplication()
-            else:
-                application = QCoreApplication.instance()
-        self._application: QCoreApplication = application  # type: ignore[assignment]
+        self._application = QCoreApplication.instance() or QCoreApplication()
 
         # Configure whether the QCoreApplication at the core of QtAsyncio
         # should be shut down when asyncio finishes. A special case where one
@@ -105,7 +104,7 @@ class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
         # this instance is shut down every time.
         self._quit_qapp = quit_qapp
 
-        self._event_loop: typing.Optional[asyncio.AbstractEventLoop] = None
+        self._event_loop: asyncio.AbstractEventLoop | None = None
 
         if handle_sigint:
             signal.signal(signal.SIGINT, signal.SIG_DFL)
@@ -115,7 +114,7 @@ class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
             self._event_loop = QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
         return self._event_loop
 
-    def set_event_loop(self, loop: typing.Optional[asyncio.AbstractEventLoop]) -> None:
+    def set_event_loop(self, loop: asyncio.AbstractEventLoop | None) -> None:
         self._event_loop = loop
 
     def new_event_loop(self) -> asyncio.AbstractEventLoop:
@@ -191,7 +190,7 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
         self._quit_from_outside = False
 
         # A set of all asynchronous generators that are currently running.
-        self._asyncgens: typing.Set[collections.abc.AsyncGenerator] = set()
+        self._asyncgens: set[collections.abc.AsyncGenerator] = set()
 
         # Starting with Python 3.11, this must be an instance of
         # ThreadPoolExecutor.
@@ -202,14 +201,14 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
         # asynchonrous generator raises an exception when closed, and two, if
         # an exception is raised during the execution of a task. Currently, the
         # default exception handler just prints the exception to the console.
-        self._exception_handler: typing.Optional[typing.Callable] = self.default_exception_handler
+        self._exception_handler: Callable | None = self.default_exception_handler
 
         # The task factory, if set with set_task_factory(). Otherwise, a new
         # task is created with the QAsyncioTask constructor.
-        self._task_factory: typing.Optional[typing.Callable] = None
+        self._task_factory: Callable | None = None
 
         # The future that is currently being awaited with run_until_complete().
-        self._future_to_complete: typing.Optional[futures.QAsyncioFuture] = None
+        self._future_to_complete: futures.QAsyncioFuture | None = None
 
         self._debug = bool(os.getenv("PYTHONASYNCIODEBUG", False))
 
@@ -228,7 +227,7 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
         future.get_loop().stop()
 
     def run_until_complete(self,
-                           future: futures.QAsyncioFuture) -> typing.Any:  # type: ignore[override]
+                           future: futures.QAsyncioFuture) -> Any:  # type: ignore[override]
         if self.is_closed():
             raise RuntimeError("Event loop is closed")
         if self.is_running():
@@ -322,7 +321,7 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
         self._asyncgens.clear()
 
     async def shutdown_default_executor(self,  # type: ignore[override]
-                                        timeout: typing.Union[int, float, None] = None) -> None:
+                                        timeout: int | float | None = None) -> None:
         shutdown_successful = False
         if timeout is not None:
             deadline_timer = QDeadlineTimer(int(timeout * 1000))
@@ -347,51 +346,46 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
 
     # Scheduling callbacks
 
-    def _call_soon_impl(self, callback: typing.Callable, *args: typing.Any,
-                        context: typing.Optional[contextvars.Context] = None,
-                        is_threadsafe: typing.Optional[bool] = False) -> asyncio.Handle:
+    def _call_soon_impl(self, callback: Callable, *args: Any,
+                        context: contextvars.Context | None = None,
+                        is_threadsafe: bool | None = False) -> asyncio.Handle:
         return self._call_later_impl(0, callback, *args, context=context,
                                      is_threadsafe=is_threadsafe)
 
-    def call_soon(self, callback: typing.Callable, *args: typing.Any,
-                  context: typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
+    def call_soon(self, callback: Callable, *args: Any,
+                  context: contextvars.Context | None = None) -> asyncio.Handle:
         return self._call_soon_impl(callback, *args, context=context, is_threadsafe=False)
 
-    def call_soon_threadsafe(self, callback: typing.Callable, *args: typing.Any,
-                             context:
-                             typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
+    def call_soon_threadsafe(self, callback: Callable, *args: Any,
+                             context: contextvars.Context | None = None) -> asyncio.Handle:
         if self.is_closed():
             raise RuntimeError("Event loop is closed")
         if context is None:
             context = contextvars.copy_context()
         return self._call_soon_impl(callback, *args, context=context, is_threadsafe=True)
 
-    def _call_later_impl(self, delay: typing.Union[int, float],
-                         callback: typing.Callable, *args: typing.Any,
-                         context: typing.Optional[contextvars.Context] = None,
-                         is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
+    def _call_later_impl(self, delay: int | float, callback: Callable, *args: Any,
+                         context: contextvars.Context | None = None,
+                         is_threadsafe: bool | None = False) -> asyncio.TimerHandle:
         if not isinstance(delay, (int, float)):
             raise TypeError("delay must be an int or float")
         return self._call_at_impl(self.time() + delay, callback, *args, context=context,
                                   is_threadsafe=is_threadsafe)
 
-    def call_later(self, delay: typing.Union[int, float],
-                   callback: typing.Callable, *args: typing.Any,
-                   context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+    def call_later(self, delay: int | float, callback: Callable, *args: Any,
+                   context: contextvars.Context | None = None) -> asyncio.TimerHandle:
         return self._call_later_impl(delay, callback, *args, context=context, is_threadsafe=False)
 
-    def _call_at_impl(self, when: typing.Union[int, float],
-                      callback: typing.Callable, *args: typing.Any,
-                      context: typing.Optional[contextvars.Context] = None,
-                      is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
+    def _call_at_impl(self, when: int | float, callback: Callable, *args: Any,
+                      context: contextvars.Context | None = None,
+                      is_threadsafe: bool | None = False) -> asyncio.TimerHandle:
         """ All call_at() and call_later() methods map to this method. """
         if not isinstance(when, (int, float)):
             raise TypeError("when must be an int or float")
         return QAsyncioTimerHandle(when, callback, args, self, context, is_threadsafe=is_threadsafe)
 
-    def call_at(self, when: typing.Union[int, float],
-                callback: typing.Callable, *args: typing.Any,
-                context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+    def call_at(self, when: int | float, callback: Callable, *args: Any,
+                context: contextvars.Context | None = None) -> asyncio.TimerHandle:
         return self._call_at_impl(when, callback, *args, context=context, is_threadsafe=False)
 
     def time(self) -> float:
@@ -403,9 +397,9 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
         return futures.QAsyncioFuture(loop=self)
 
     def create_task(self,  # type: ignore[override]
-                    coro: typing.Union[collections.abc.Generator, collections.abc.Coroutine],
-                    *, name: typing.Optional[str] = None,
-                    context: typing.Optional[contextvars.Context] = None) -> tasks.QAsyncioTask:
+                    coro: collections.abc.Generator | collections.abc.Coroutine,
+                    *, name: str | None = None,
+                    context: contextvars.Context | None = None) -> tasks.QAsyncioTask:
         if self._task_factory is None:
             task = tasks.QAsyncioTask(coro, loop=self, name=name, context=context)
         else:
@@ -414,12 +408,12 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
 
         return task
 
-    def set_task_factory(self, factory: typing.Optional[typing.Callable]) -> None:
+    def set_task_factory(self, factory: Callable | None) -> None:
         if factory is not None and not callable(factory):
             raise TypeError("The task factory must be a callable or None")
         self._task_factory = factory
 
-    def get_task_factory(self) -> typing.Optional[typing.Callable]:
+    def get_task_factory(self) -> Callable | None:
         return self._task_factory
 
     # Opening network connections
@@ -432,14 +426,15 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
             ssl_handshake_timeout=None,
             ssl_shutdown_timeout=None,
             happy_eyeballs_delay=None, interleave=None):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.create_connection() is not implemented yet")
 
     async def create_datagram_endpoint(self, protocol_factory,
                                        local_addr=None, remote_addr=None, *,
                                        family=0, proto=0, flags=0,
                                        reuse_address=None, reuse_port=None,
                                        allow_broadcast=None, sock=None):
-        raise NotImplementedError
+        raise NotImplementedError(
+            "QAsyncioEventLoop.create_datagram_endpoint() is not implemented yet")
 
     async def create_unix_connection(
             self, protocol_factory, path=None, *,
@@ -447,7 +442,8 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
             server_hostname=None,
             ssl_handshake_timeout=None,
             ssl_shutdown_timeout=None):
-        raise NotImplementedError
+        raise NotImplementedError(
+            "QAsyncioEventLoop.create_unix_connection() is not implemented yet")
 
     # Creating network servers
 
@@ -459,7 +455,7 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
             ssl_handshake_timeout=None,
             ssl_shutdown_timeout=None,
             start_serving=True):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.create_server() is not implemented yet")
 
     async def create_unix_server(
             self, protocol_factory, path=None, *,
@@ -467,20 +463,21 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
             ssl_handshake_timeout=None,
             ssl_shutdown_timeout=None,
             start_serving=True):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.create_unix_server() is not implemented yet")
 
     async def connect_accepted_socket(
             self, protocol_factory, sock,
             *, ssl=None,
             ssl_handshake_timeout=None,
             ssl_shutdown_timeout=None):
-        raise NotImplementedError
+        raise NotImplementedError(
+            "QAsyncioEventLoop.connect_accepted_socket() is not implemented yet")
 
     # Transferring files
 
     async def sendfile(self, transport, file, offset=0, count=None,
                        *, fallback=True):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sendfile() is not implemented yet")
 
     # TLS Upgrade
 
@@ -489,82 +486,83 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
                         server_hostname=None,
                         ssl_handshake_timeout=None,
                         ssl_shutdown_timeout=None):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.start_tls() is not implemented yet")
 
     # Watching file descriptors
 
     def add_reader(self, fd, callback, *args):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.add_reader() is not implemented yet")
 
     def remove_reader(self, fd):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.remove_reader() is not implemented yet")
 
     def add_writer(self, fd, callback, *args):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.add_writer() is not implemented yet")
 
     def remove_writer(self, fd):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.remove_writer() is not implemented yet")
 
     # Working with socket objects directly
 
     async def sock_recv(self, sock, nbytes):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_recv() is not implemented yet")
 
     async def sock_recv_into(self, sock, buf):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_recv_into() is not implemented yet")
 
     async def sock_recvfrom(self, sock, bufsize):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_recvfrom() is not implemented yet")
 
     async def sock_recvfrom_into(self, sock, buf, nbytes=0):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_recvfrom_into() is not implemented yet")
 
     async def sock_sendall(self, sock, data):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_sendall() is not implemented yet")
 
     async def sock_sendto(self, sock, data, address):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_sendto() is not implemented yet")
 
     async def sock_connect(self, sock, address):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_connect() is not implemented yet")
 
     async def sock_accept(self, sock):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_accept() is not implemented yet")
 
     async def sock_sendfile(self, sock, file, offset=0, count=None, *,
                             fallback=None):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.sock_sendfile() is not implemented yet")
 
     # DNS
 
     async def getaddrinfo(self, host, port, *,
                           family=0, type=0, proto=0, flags=0):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.getaddrinfo() is not implemented yet")
 
     async def getnameinfo(self, sockaddr, flags=0):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.getnameinfo() is not implemented yet")
 
     # Working with pipes
 
     async def connect_read_pipe(self, protocol_factory, pipe):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.connect_read_pipe() is not implemented yet")
 
     async def connect_write_pipe(self, protocol_factory, pipe):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.connect_write_pipe() is not implemented yet")
 
     # Unix signals
 
     def add_signal_handler(self, sig, callback, *args):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.add_signal_handler() is not implemented yet")
 
     def remove_signal_handler(self, sig):
-        raise NotImplementedError
+        raise NotImplementedError(
+            "QAsyncioEventLoop.remove_signal_handler() is not implemented yet")
 
     # Executing code in thread or process pools
 
     def run_in_executor(self,
-                        executor: typing.Optional[concurrent.futures.ThreadPoolExecutor],
-                        func: typing.Callable, *args: typing.Tuple) -> asyncio.futures.Future:
+                        executor: concurrent.futures.ThreadPoolExecutor | None,
+                        func: Callable, *args: tuple) -> asyncio.futures.Future:
         if self.is_closed():
             raise RuntimeError("Event loop is closed")
         if executor is None:
@@ -582,28 +580,27 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
         )
 
     def set_default_executor(self,
-                             executor: typing.Optional[
-                                 concurrent.futures.ThreadPoolExecutor]) -> None:
+                             executor: concurrent.futures.ThreadPoolExecutor | None) -> None:
         if not isinstance(executor, concurrent.futures.ThreadPoolExecutor):
             raise TypeError("The executor must be a ThreadPoolExecutor")
         self._default_executor = executor
 
     # Error Handling API
 
-    def set_exception_handler(self, handler: typing.Optional[typing.Callable]) -> None:
+    def set_exception_handler(self, handler: Callable | None) -> None:
         if handler is not None and not callable(handler):
             raise TypeError("The handler must be a callable or None")
         self._exception_handler = handler
 
-    def get_exception_handler(self) -> typing.Optional[typing.Callable]:
+    def get_exception_handler(self) -> Callable | None:
         return self._exception_handler
 
-    def default_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
+    def default_exception_handler(self, context: dict[str, Any]) -> None:
         # TODO
         if context["message"]:
             print(context["message"])
 
-    def call_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
+    def call_exception_handler(self, context: dict[str, Any]) -> None:
         if self._exception_handler is not None:
             self._exception_handler(context)
 
@@ -624,14 +621,14 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               **kwargs):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.subprocess_exec() is not implemented yet")
 
     async def subprocess_shell(self, protocol_factory, cmd, *,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                **kwargs):
-        raise NotImplementedError
+        raise NotImplementedError("QAsyncioEventLoop.subprocess_shell() is not implemented yet")
 
 
 class QAsyncioHandle():
@@ -646,9 +643,9 @@ class QAsyncioHandle():
         CANCELLED = enum.auto()
         DONE = enum.auto()
 
-    def __init__(self, callback: typing.Callable, args: typing.Tuple,
-                 loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
-                 is_threadsafe: typing.Optional[bool] = False) -> None:
+    def __init__(self, callback: Callable, args: tuple,
+                 loop: QAsyncioEventLoop, context: contextvars.Context | None,
+                 is_threadsafe: bool | None = False) -> None:
         self._callback = callback
         self._args = args
         self._loop = loop
@@ -663,7 +660,7 @@ class QAsyncioHandle():
     def _start(self) -> None:
         self._schedule_event(self._timeout, lambda: self._cb())
 
-    def _schedule_event(self, timeout: int, func: typing.Callable) -> None:
+    def _schedule_event(self, timeout: int, func: Callable) -> None:
         # Do not schedule events from asyncio when the app is quit from outside
         # the event loop, as this would cause events to be enqueued after the
         # event loop was destroyed.
@@ -703,13 +700,17 @@ class QAsyncioHandle():
 
 
 class QAsyncioTimerHandle(QAsyncioHandle, asyncio.TimerHandle):
-    def __init__(self, when: float, callback: typing.Callable, args: typing.Tuple,
-                 loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
-                 is_threadsafe: typing.Optional[bool] = False) -> None:
+    def __init__(self, when: float, callback: Callable, args: tuple,
+                 loop: QAsyncioEventLoop, context: contextvars.Context | None,
+                 is_threadsafe: bool | None = False) -> None:
         QAsyncioHandle.__init__(self, callback, args, loop, context, is_threadsafe)
 
         self._when = when
         time = self._loop.time()
+
+        # PYSIDE-2644: Timeouts should be rounded up or down instead of only up
+        # as happens with int(). Otherwise, a timeout of e.g. 0.9 would be
+        # handled as 0, where 1 would be more appropriate.
         self._timeout = round(max(self._when - time, 0) * 1000)
 
         QAsyncioHandle._start(self)
index cbb005fc92ee9d85e3d739212225501b75947cea..6b4415490cdf0e42ed04e4aa9b713d8570dcff4a 100644 (file)
@@ -1,12 +1,14 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from . import events
 
+from typing import Any, Callable
+
 import asyncio
 import contextvars
 import enum
-import typing
 
 
 class QAsyncioFuture():
@@ -23,8 +25,8 @@ class QAsyncioFuture():
         DONE_WITH_RESULT = enum.auto()
         DONE_WITH_EXCEPTION = enum.auto()
 
-    def __init__(self, *, loop: typing.Optional["events.QAsyncioEventLoop"] = None,
-                 context: typing.Optional[contextvars.Context] = None) -> None:
+    def __init__(self, *, loop: "events.QAsyncioEventLoop | None" = None,
+                 context: contextvars.Context | None = None) -> None:
         self._loop: "events.QAsyncioEventLoop"
         if loop is None:
             self._loop = asyncio.events.get_event_loop()  # type: ignore[assignment]
@@ -33,13 +35,13 @@ class QAsyncioFuture():
         self._context = context
 
         self._state = QAsyncioFuture.FutureState.PENDING
-        self._result: typing.Any = None
-        self._exception: typing.Optional[BaseException] = None
+        self._result: Any = None
+        self._exception: BaseException | None = None
 
-        self._cancel_message: typing.Optional[str] = None
+        self._cancel_message: str | None = None
 
         # List of callbacks that are called when the future is done.
-        self._callbacks: typing.List[typing.Callable] = list()
+        self._callbacks: list[Callable] = list()
 
     def __await__(self):
         if not self.done():
@@ -51,13 +53,13 @@ class QAsyncioFuture():
 
     __iter__ = __await__
 
-    def _schedule_callbacks(self, context: typing.Optional[contextvars.Context] = None):
+    def _schedule_callbacks(self, context: contextvars.Context | None = None):
         """ A future can optionally have callbacks that are called when the future is done. """
         for cb in self._callbacks:
             self._loop.call_soon(
                 cb, self, context=context if context else self._context)
 
-    def result(self) -> typing.Union[typing.Any, Exception]:
+    def result(self) -> Any | Exception:
         if self._state == QAsyncioFuture.FutureState.DONE_WITH_RESULT:
             return self._result
         if self._state == QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION and self._exception:
@@ -69,7 +71,7 @@ class QAsyncioFuture():
                 raise asyncio.CancelledError
         raise asyncio.InvalidStateError
 
-    def set_result(self, result: typing.Any) -> None:
+    def set_result(self, result: Any) -> None:
         self._result = result
         self._state = QAsyncioFuture.FutureState.DONE_WITH_RESULT
         self._schedule_callbacks()
@@ -85,20 +87,20 @@ class QAsyncioFuture():
     def cancelled(self) -> bool:
         return self._state == QAsyncioFuture.FutureState.CANCELLED
 
-    def add_done_callback(self, cb: typing.Callable, *,
-                          context: typing.Optional[contextvars.Context] = None) -> None:
+    def add_done_callback(self, cb: Callable, *,
+                          context: contextvars.Context | None = None) -> None:
         if self.done():
             self._loop.call_soon(
                 cb, self, context=context if context else self._context)
         else:
             self._callbacks.append(cb)
 
-    def remove_done_callback(self, cb: typing.Callable) -> int:
+    def remove_done_callback(self, cb: Callable) -> int:
         original_len = len(self._callbacks)
         self._callbacks = [_cb for _cb in self._callbacks if _cb != cb]
         return original_len - len(self._callbacks)
 
-    def cancel(self, msg: typing.Optional[str] = None) -> bool:
+    def cancel(self, msg: str | None = None) -> bool:
         if self.done():
             return False
         self._state = QAsyncioFuture.FutureState.CANCELLED
@@ -106,7 +108,7 @@ class QAsyncioFuture():
         self._schedule_callbacks()
         return True
 
-    def exception(self) -> typing.Optional[BaseException]:
+    def exception(self) -> BaseException | None:
         if self._state == QAsyncioFuture.FutureState.CANCELLED:
             raise asyncio.CancelledError
         if self.done():
index 7edc15093284ca1734c94b77f654201984e4b1d8..be1809d5c3aa85e14fc99d5b192041430c61ff89 100644 (file)
@@ -1,23 +1,24 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from . import events
 from . import futures
 
+from typing import Any
+
 import asyncio
 import collections.abc
 import concurrent.futures
 import contextvars
-import typing
 
 
 class QAsyncioTask(futures.QAsyncioFuture):
     """ https://docs.python.org/3/library/asyncio-task.html """
 
-    def __init__(self, coro: typing.Union[collections.abc.Generator, collections.abc.Coroutine], *,
-                 loop: typing.Optional["events.QAsyncioEventLoop"] = None,
-                 name: typing.Optional[str] = None,
-                 context: typing.Optional[contextvars.Context] = None) -> None:
+    def __init__(self, coro: collections.abc.Generator | collections.abc.Coroutine, *,
+                 loop: "events.QAsyncioEventLoop | None" = None, name: str | None = None,
+                 context: contextvars.Context | None = None) -> None:
         super().__init__(loop=loop, context=context)
 
         self._coro = coro  # The coroutine for which this task was created.
@@ -25,15 +26,18 @@ class QAsyncioTask(futures.QAsyncioFuture):
 
         # The task creates a handle for its coroutine. The handle enqueues the
         # task's step function as its callback in the event loop.
-        self._handle = self._loop.call_soon(self._step, context=self._context)
+        self._loop.call_soon(self._step, context=self._context)
 
         # The task step function executes the coroutine until it finishes,
         # raises an exception or returns a future. If a future was returned,
-        # the task will await its completion (or exception).
-        self._future_to_await: typing.Optional[asyncio.Future] = None
+        # the task will await its completion (or exception). If the task is
+        # cancelled while it awaits a future, this future must also be
+        # cancelled in order for the cancellation to be successful.
+        self._future_to_await: asyncio.Future | None = None
 
-        self._cancelled = False
-        self._cancel_message: typing.Optional[str] = None
+        self._cancelled = False  # PYSIDE-2644; see _step
+        self._cancel_count = 0
+        self._cancel_message: str | None = None
 
         # https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
         asyncio._register_task(self)  # type: ignore[arg-type]
@@ -53,17 +57,16 @@ class QAsyncioTask(futures.QAsyncioFuture):
     class QtTaskApiMisuseError(Exception):
         pass
 
-    def set_result(self, result: typing.Any) -> None:  # type: ignore[override]
+    def set_result(self, result: Any) -> None:  # type: ignore[override]
         # This function is not inherited from the Future APIs.
         raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set results")
 
-    def set_exception(self, exception: typing.Any) -> None:  # type: ignore[override]
+    def set_exception(self, exception: Any) -> None:  # type: ignore[override]
         # This function is not inherited from the Future APIs.
         raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set exceptions")
 
     def _step(self,
-              exception_or_future: typing.Union[
-                  BaseException, futures.QAsyncioFuture, None] = None) -> None:
+              exception_or_future: BaseException | futures.QAsyncioFuture | None = None) -> None:
         """
         The step function is the heart of a task. It is scheduled in the event
         loop repeatedly, executing the coroutine "step" by "step" (i.e.,
@@ -77,6 +80,10 @@ class QAsyncioTask(futures.QAsyncioFuture):
         result = None
         self._future_to_await = None
 
+        if self._cancelled:
+            exception_or_future = asyncio.CancelledError(self._cancel_message)
+            self._cancelled = False
+
         if asyncio.futures.isfuture(exception_or_future):
             try:
                 exception_or_future.result()
@@ -113,11 +120,17 @@ class QAsyncioTask(futures.QAsyncioFuture):
                 # called again.
                 result.add_done_callback(
                     self._step, context=self._context)  # type: ignore[arg-type]
+
+                # The task will await the completion (or exception) of this
+                # future. If the task is cancelled while it awaits a future,
+                # this future must also be cancelled.
                 self._future_to_await = result
+
                 if self._cancelled:
-                    # If the task was cancelled, then a new future should be
-                    # cancelled as well. Otherwise, in some scenarios like
-                    # a loop inside the task and with bad timing, if the new
+                    # PYSIDE-2644: If the task was cancelled at this step and a
+                    # new future was created to be awaited, then it should be
+                    # cancelled as well. Otherwise, in some scenarios like a
+                    # loop inside the task and with bad timing, if the new
                     # future is not cancelled, the task would continue running
                     # in this loop despite having been cancelled. This bad
                     # timing can occur especially if the first future finishes
@@ -135,10 +148,13 @@ class QAsyncioTask(futures.QAsyncioFuture):
             asyncio._leave_task(self._loop, self)  # type: ignore[arg-type]
 
             if self._exception:
+                message = str(self._exception)
+                if message == "None":
+                    message = ""
+                else:
+                    message = "An exception occurred during task execution"
                 self._loop.call_exception_handler({
-                    "message": (str(self._exception) if self._exception
-                                else "An exception occurred during task "
-                                "execution"),
+                    "message": message,
                     "exception": self._exception,
                     "task": self,
                     "future": (exception_or_future
@@ -152,7 +168,7 @@ class QAsyncioTask(futures.QAsyncioFuture):
                 # https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
                 asyncio._unregister_task(self)  # type: ignore[arg-type]
 
-    def get_stack(self, *, limit=None) -> typing.List[typing.Any]:
+    def get_stack(self, *, limit=None) -> list[Any]:
         # TODO
         raise NotImplementedError("QtTask.get_stack is not implemented")
 
@@ -160,7 +176,7 @@ class QAsyncioTask(futures.QAsyncioFuture):
         # TODO
         raise NotImplementedError("QtTask.print_stack is not implemented")
 
-    def get_coro(self) -> typing.Union[collections.abc.Generator, collections.abc.Coroutine]:
+    def get_coro(self) -> collections.abc.Generator | collections.abc.Coroutine:
         return self._coro
 
     def get_name(self) -> str:
@@ -169,22 +185,22 @@ class QAsyncioTask(futures.QAsyncioFuture):
     def set_name(self, value) -> None:
         self._name = str(value)
 
-    def cancel(self, msg: typing.Optional[str] = None) -> bool:
+    def cancel(self, msg: str | None = None) -> bool:
         if self.done():
             return False
+        self._cancel_count += 1
         self._cancel_message = msg
-        self._handle.cancel()
         if self._future_to_await is not None:
             # A task that is awaiting a future must also cancel this future in
             # order for the cancellation to be successful.
             self._future_to_await.cancel(msg)
-        self._cancelled = True
+        self._cancelled = True  # PYSIDE-2644; see _step
         return True
 
-    def uncancel(self) -> None:
-        # TODO
-        raise NotImplementedError("QtTask.uncancel is not implemented")
+    def uncancel(self) -> int:
+        if self._cancel_count > 0:
+            self._cancel_count -= 1
+        return self._cancel_count
 
-    def cancelling(self) -> bool:
-        # TODO
-        raise NotImplementedError("QtTask.cancelling is not implemented")
+    def cancelling(self) -> int:
+        return self._cancel_count
index f3e4be0f9dfa19cb3ac33429b970e5efe254f58c..d985b9778dd766050a86dea099d21388af88f591 100644 (file)
@@ -67,6 +67,8 @@ ${QtCore_GEN_DIR}/qdatetime_wrapper.cpp
 ${QtCore_GEN_DIR}/qdeadlinetimer_wrapper.cpp
 ${QtCore_GEN_DIR}/qdir_wrapper.cpp
 ${QtCore_GEN_DIR}/qdiriterator_wrapper.cpp
+${QtCore_GEN_DIR}/qdirlisting_wrapper.cpp
+${QtCore_GEN_DIR}/qdirlisting_direntry_wrapper.cpp
 ${QtCore_GEN_DIR}/qdynamicpropertychangeevent_wrapper.cpp
 ${QtCore_GEN_DIR}/qeasingcurve_wrapper.cpp
 ${QtCore_GEN_DIR}/qelapsedtimer_wrapper.cpp
@@ -116,6 +118,7 @@ ${QtCore_GEN_DIR}/qmimedatabase_wrapper.cpp
 ${QtCore_GEN_DIR}/qmimetype_wrapper.cpp
 ${QtCore_GEN_DIR}/qmodelindex_wrapper.cpp
 ${QtCore_GEN_DIR}/qmodelroledata_wrapper.cpp
+${QtCore_GEN_DIR}/qmodelroledataspan_wrapper.cpp
 ${QtCore_GEN_DIR}/qmutex_wrapper.cpp
 ${QtCore_GEN_DIR}/qnativeipckey_wrapper.cpp
 ${QtCore_GEN_DIR}/qobject_wrapper.cpp
@@ -165,6 +168,7 @@ ${QtCore_GEN_DIR}/qstringlistmodel_wrapper.cpp
 ${QtCore_GEN_DIR}/qsysinfo_wrapper.cpp
 ${QtCore_GEN_DIR}/qsystemsemaphore_wrapper.cpp
 ${QtCore_GEN_DIR}/qt_wrapper.cpp
+${QtCore_GEN_DIR}/qtcorehelper_qdirlistingiterator_wrapper.cpp
 ${QtCore_GEN_DIR}/qtcorehelper_qgenericargumentholder_wrapper.cpp
 ${QtCore_GEN_DIR}/qtcorehelper_qgenericreturnargumentholder_wrapper.cpp
 ${QtCore_GEN_DIR}/qtcorehelper_qiopipe_wrapper.cpp
index f6acf9d60703fcc9cad76a621fc84e7b21dbcb78..c073c8bc10cfc63cc0056090f462add09667fda4 100644 (file)
@@ -45,8 +45,8 @@ QMetaType QVariant_resolveMetaType(PyTypeObject *type)
     // that has added any python fields or slots to its object layout.
     // See https://mail.python.org/pipermail/python-list/2009-January/520733.html
     if (type->tp_bases) {
-        for (Py_ssize_t i = 0, size = PyTuple_GET_SIZE(type->tp_bases); i < size; ++i) {
-            auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(type->tp_bases, i));
+        for (Py_ssize_t i = 0, size = PyTuple_Size(type->tp_bases); i < size; ++i) {
+            auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, i));
             const QMetaType derived = QVariant_resolveMetaType(baseType);
             if (derived.isValid())
                 return derived;
@@ -103,9 +103,9 @@ bool QVariant_isStringList(PyObject *list)
     }
 
     Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList"));
-    const Py_ssize_t size = PySequence_Fast_GET_SIZE(fast.object());
+    const Py_ssize_t size = PySequence_Size(fast.object());
     for (Py_ssize_t i = 0; i < size; ++i) {
-        PyObject *item = PySequence_Fast_GET_ITEM(fast.object(), i);
+        Shiboken::AutoDecRef item(PySequence_GetItem(fast.object(), i));
         if (PyUnicode_Check(item) == 0)
             return false;
     }
@@ -224,13 +224,13 @@ void qObjectFindChildren(const QObject *parent, const QRegularExpression &patter
 QString qObjectTr(PyTypeObject *type, const char *sourceText, const char *disambiguation, int n)
 {
     PyObject *mro = type->tp_mro;
-    auto len = PyTuple_GET_SIZE(mro);
+    auto len = PyTuple_Size(mro);
     QString result = QString::fromUtf8(sourceText);
     QString oldResult = result;
     static auto *sbkObjectType = reinterpret_cast<PyTypeObject *>(SbkObject_TypeF());
     for (Py_ssize_t idx = 0; idx < len - 1; ++idx) {
         // Skip the last class which is `object`.
-        auto *type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+        auto *type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
         if (type == sbkObjectType)
             continue;
         const char *context = type->tp_name;
index e154b932a9a48ae13566bced7541020867c2ccd8..d806b23e17ca698f5a9baeb99fa8b79e6495bdfa 100644 (file)
@@ -69,7 +69,7 @@ void PySideEasingCurveFunctor::init()
     CustomFunctions<MAX_CUSTOM_FUNCTIONS-1>::init();
 }
 
-QEasingCurve::EasingFunction PySideEasingCurveFunctor::createCustomFuntion(PyObject *parent, PyObject *pyFunc)
+QEasingCurve::EasingFunction PySideEasingCurveFunctor::createCustomFunction(PyObject *parent, PyObject *pyFunc)
 {
     for(int i=0; i < MAX_CUSTOM_FUNCTIONS; i++) {
         CustomFunctionsData &data = CustomFunctionsData::m_list[i];
@@ -120,4 +120,3 @@ PySideEasingCurveFunctor::PySideEasingCurveFunctor(int index, PyObject *parent,
     PyObject_SetAttr(m_parent, Shiboken::PyMagicName::ecf(), m_func);
     PySide::WeakRef::create(m_parent, deleteData, this);
 }
-
index 78adaae2ef74d867dab96331f91f31072a136f43..181a205f017fda234acb8072ac4adc3714de4733 100644 (file)
@@ -11,7 +11,7 @@ class PySideEasingCurveFunctor
 {
     public:
         static void init();
-        static QEasingCurve::EasingFunction createCustomFuntion(PyObject *parent, PyObject *pyFunc);
+        static QEasingCurve::EasingFunction createCustomFunction(PyObject *parent, PyObject *pyFunc);
 
         qreal operator()(qreal progress);
 
index 948c0ce5c86b4fac9628e17c4450f378374aaebc..a56417acf37d0c8c931771e7f3f6d4cad39e7eeb 100644 (file)
@@ -103,6 +103,72 @@ const void *QGenericReturnArgumentHolder::data() const
     return d->m_argument.data();
 }
 
+// QDirListing::const_iterator has no copy semantics (shared internal state, QTBUG-125512).
+// The Python iterable semantics (calling __next__() first before retrieving the first value)
+// can therefore not be implemented as "return iterator++;". Wrap a helper class
+// around it that does a no-op in the first call to __next__().
+struct QDirListingIteratorPrivate
+{
+    enum State { First, Iterating, End };
+
+    explicit QDirListingIteratorPrivate(const QDirListing &dl) :
+        iterator(dl.cbegin()), state(First) {}
+    QDirListingIteratorPrivate() : state(End) {}
+
+    bool next();
+
+    QDirListing::const_iterator iterator;
+    State state;
+};
+
+inline bool QDirListingIteratorPrivate::next()
+{
+    switch (state) {
+    case First:
+        state = iterator != QDirListing::sentinel{} ? Iterating : End;
+        break;
+    case Iterating:
+        if (++iterator == QDirListing::sentinel{})
+            state = End;
+        break;
+    case End:
+        break;
+    }
+    return state != End;
+}
+
+QDirListingIterator::QDirListingIterator(const QDirListing &dl) :
+    d(std::make_shared<QDirListingIteratorPrivate>(dl))
+{
+}
+
+QDirListingIterator::QDirListingIterator() :
+    d(std::make_shared<QDirListingIteratorPrivate>())
+{
+}
+
+QDirListingIterator::QDirListingIterator(const QDirListingIterator &) = default;
+QDirListingIterator &QDirListingIterator::operator=(const QDirListingIterator &) = default;
+QDirListingIterator::QDirListingIterator(QDirListingIterator &&) noexcept = default;
+QDirListingIterator &QDirListingIterator::operator=(QDirListingIterator &&) noexcept = default;
+QDirListingIterator::~QDirListingIterator() = default;
+
+bool QDirListingIterator::next()
+{
+    return d->next();
+}
+
+const QDirListing::DirEntry &QDirListingIterator::value() const
+{
+    Q_ASSERT(d->state == QDirListingIteratorPrivate::Iterating);
+    return *d->iterator;
+}
+
+bool QDirListingIterator::atEnd() const
+{
+    return d->state == QDirListingIteratorPrivate::End;
+}
+
 } // namespace QtCoreHelper
 
 QT_END_NAMESPACE
index 19a50848ff11ed2cc4dda9657458575e75d8f73b..cbcbbf8f6eb6570daf1b3f7a9834a74b8ebb18cd 100644 (file)
     <inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
                  snippet="darwin_permission_plugin"/>
 
-    <add-function signature="qDebug(const char*)">
-        <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/>
+    <add-function signature="qDebug(const char*@message@)">
+        <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
         <modify-argument index="1" pyi-type="str"/>
     </add-function>
-    <add-function signature="qCritical(const char*)">
-        <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/>
+    <add-function signature="qCritical(const char*@message@)">
+        <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
         <modify-argument index="1" pyi-type="str"/>
     </add-function>
-    <add-function signature="qFatal(const char*)">
+    <add-function signature="qFatal(const char*@message@)">
         <inject-code file="../glue/qtcore.cpp" snippet="qfatal"/>
         <modify-argument index="1" pyi-type="str"/>
     </add-function>
-    <add-function signature="qWarning(const char*)">
-        <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/>
+    <add-function signature="qInfo(const char*@message@)">
+        <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
+        <modify-argument index="1" pyi-type="str"/>
+    </add-function>
+    <add-function signature="qWarning(const char*@message@)">
+        <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
         <modify-argument index="1" pyi-type="str"/>
     </add-function>
 
     <add-function signature="Q_ARG(PyObject *@type@, PyObject *@value@)"
                   return-type="QtCoreHelper::QGenericArgumentHolder">
         <inject-code file="../glue/qtcore.cpp" snippet="q_arg"/>
-        <inject-documentation format="target" mode="append">
-        This function takes a type (or a type string) and a value of that type
-        and returns an internal object that can be passed to
-        QMetaObject.invokeMethod(). See also Q_RETURN_ARG().
-        </inject-documentation>
+        <inject-documentation format="target" mode="append"
+                              file="../doc/qtcore.rst" snippet="q_arg"/>
     </add-function>
     <add-function signature="Q_RETURN_ARG(PyObject *@type@)"
                   return-type="QtCoreHelper::QGenericReturnArgumentHolder">
         <inject-code file="../glue/qtcore.cpp" snippet="q_return_arg"/>
-        <inject-documentation format="target" mode="append">
-        This macro takes a type (or a type string) a value of which is then
-        returned by QMetaObject::invokeMethod(). See also Q_ARG().
-        </inject-documentation>
+        <inject-documentation format="target" mode="append"
+                              file="../doc/qtcore.rst" snippet="q_return_arg"/>
     </add-function>
 
     <!-- TODO: We do not support void* or const void* as arg -->
       </extra-includes>
       <enum-type name="Type" python-type="IntEnum"/>
       <enum-type name="TypeFlag" flags="TypeFlags"/>
-      <add-function signature="QMetaType(PyTypeObject*)">
+      <add-function signature="QMetaType(PyTypeObject*@py_type@)">
           <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
                        snippet="metatype-from-type"/>
       </add-function>
       <!-- PYSIDE-1735: The class QMetaType.Type must be allowed because int is gone -->
-      <add-function signature="QMetaType(QMetaType::Type)">
+      <add-function signature="QMetaType(QMetaType::Type@type@)">
           <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
                        snippet="metatype-from-metatype-type"/>
       </add-function>
     <enum-type name="ClipOperation"/>
     <enum-type name="ConnectionType"/>
     <enum-type name="ContextMenuPolicy"/>
+    <enum-type name="ContextMenuTrigger" since="6.8"/>
     <enum-type name="CoordinateSystem" since="4.6"/>
     <enum-type name="Corner"/>
     <enum-type name="CursorShape"/>
     <enum-type name="TextFormat"/>
     <enum-type name="TextInteractionFlag" flags="TextInteractionFlags"/>
     <enum-type name="TileRule" since="4.6"/>
+    <enum-type name="TimerId" since="6.8"/>
     <enum-type name="TimerType"/>
     <enum-type name="TimeSpec"/>
     <enum-type name="ToolBarArea" flags="ToolBarAreas"/>
     <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-qabs"/>
   </add-function>
 
-  <add-function signature="qAddPostRoutine(PyObject*)">
+  <add-function signature="qAddPostRoutine(PyObject*@callable@)">
     <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-addpostroutine"/>
   </add-function>
   <inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="qt-qaddpostroutine"/>
   <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qt-messagehandler"/>
   <inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
                snippet="qlatin1string-check"/>
-  <add-function signature="qInstallMessageHandler(PyObject)" return-type="PyObject">
+  <add-function signature="qInstallMessageHandler(PyObject@callable@)" return-type="PyObject">
     <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-installmessagehandler"/>
   </add-function>
 
     <extra-includes>
       <include file-name="pysideqhash.h" location="global"/>
     </extra-includes>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%i, %i, %i, %i"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="iiii"/>
   </value-type>
   <value-type name="QLineF">
     <enum-type name="IntersectionType"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="dddd"/>
   <object-type name="QResource">
     <enum-type name="Compression"/>
     <modify-function signature="data()const">
-        <inject-documentation format="target">
+        <inject-documentation format="target" emphasis="language-note">
             Returns a read only buffer object pointing to the segment of data that this resource represents. If the resource is compressed the data returns is compressed and qUncompress() must be used to access the data. If the resource is a directory None is returned.
         </inject-documentation>
         <modify-argument index="return">
     <extra-includes>
       <include file-name="datetime.h" location="global"/>
     </extra-includes>
-    <add-function signature="__repr__" return-type="PyObject">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%i, %i, %i"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="iii"/>
       <include file-name="datetime.h" location="global"/>
     </extra-includes>
     <!-- Somewhat internal constructor used to pickle QDateTime -->
-    <add-function signature="QDateTime(int,int,int,int,int,int,int,int=Qt::LocalTime)">
+    <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@,int@ms@,int@spec@=Qt::LocalTime)">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-1"/>
     </add-function>
-    <add-function signature="QDateTime(int,int,int,int,int,int)">
+    <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@)">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-2"/>
     </add-function>
     <!-- PYSIDE-1735: Qt::TimeSpec is no more compatible with int -->
-    <add-function signature="QDateTime(int,int,int,int,int,int,int,Qt::TimeSpec=Qt::LocalTime)">
+    <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@,int@ms@,Qt::TimeSpec@spec@=Qt::LocalTime)">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-3"/>
     </add-function>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%i, %i, %i, %i, %i, %i, %i, %i"/>
              </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="iiiiiiii"/>
     </modify-function>
     <!-- PYSIDE-1499: End of insertion -->
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="s"/>
     </add-function>
   </value-type>
 
+  <object-type name="QDirListing" since="6.8">
+    <extra-includes>
+      <include file-name="qtcorehelper.h" location="local"/>
+    </extra-includes>
+    <enum-type name="IteratorFlag" flags="IteratorFlags"/>
+    <value-type name="DirEntry">
+      <add-function signature="__repr__" return-type="PyObject*">
+          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+                       snippet="qdirlisting-direntry-repr"/>
+      </add-function>
+    </value-type>
+    <add-function signature="__iter__()" return-type="PyObject*">
+        <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+                     snippet="qdirlisting-iter"/>
+    </add-function>
+  </object-type>
+
   <value-type name="QPermission" since="6.5">
       <configuration condition="QT_CONFIG(permissions)"/>
   </value-type>
   </value-type>
 
   <value-type name="QPoint">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%i, %i"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="ii"/>
     <!--### -->
   </value-type>
   <value-type name="QPointF">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="dd"/>
     <!--### -->
   </value-type>
   <value-type name="QRect">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%i, %i, %i, %i"/>
              </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="iiii"/>
     </modify-function>
   </value-type>
   <value-type name="QRectF">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
          FIXME These functions return qreal. Will convert to double (format
          string) mess things up in other architectures?
     -->
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="dddd"/>
     </modify-function>
   </value-type>
   <value-type name="QSize">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%i, %i"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="ii"/>
     <!--### -->
   </value-type>
   <value-type name="QSizeF">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="dd"/>
       <include file-name="datetime.h" location="global"/>
     </extra-includes>
 
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%i, %i, %i, %i"/>
              </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="iiii"/>
     <enum-type name="StringFormat"/>
     <enum-type name="Variant"/>
     <enum-type name="Version"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="'%s'"/>
              </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="s"/>
     <add-function signature="system()" return-type="QLocale" static="yes">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
          snippet="qlocale_system"/>
-        <inject-documentation mode="append" format="target">
-          Returns a QLocale object initialized to the system locale.
-
-          The system locale may use system-specific sources for locale data, where available,
-          otherwise falling back on QLocale's built-in database entry for the language, script and
-          territory the system reports.
-
-          For example, on Windows, this locale will use the decimal/grouping characters and
-          date/time formats specified in the system configuration panel.
-
-          .. note:: Qt for Python on macOS will not reflect the user's region and language
-             preferences though QLocale::system(), but will instead reflect the environment
-             variables POSIX uses to specify locale, similar to Python's locale module. If the
-             system locale cannot be determined, which can be due to none of the variables 'LC_ALL',
-             'LC_CTYPE', 'LANG' or 'LANGUAGE' being set by your environment, then the default POSIX
-             locale or 'C' locale is returned.
-
-          See also c().
-        </inject-documentation>
+        <inject-documentation mode="append" format="target"
+                              file="../doc/qtcore.rst" snippet="qlocale-system"/>
     </add-function>
     <modify-function signature="toTime(QString,QLocale::FormatType)const">
         <modify-argument index="2">
     <modify-function signature="^to.*\(.*QStringView.*$" remove="all"/>
   </value-type>
   <value-type name="QBitArray">
-    <add-function signature="__len__">
+    <add-function signature="__len__" return-type="int">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbitarray-len"/>
     </add-function>
     <add-function signature="__getitem__">
     <modify-function signature="exit(int)" allow-thread="yes"/>
   </object-type>
 
-  <value-type name="QModelRoleData"/>
+  <value-type name="QModelRoleData">
+      <add-function signature="setData(const QVariant &amp;@data@)">
+          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+                       snippet="qmodelroledata-setdata"/>
+      </add-function>
+  </value-type>
+
+  <value-type name="QModelRoleDataSpan">
+      <modify-function signature="dataForRole(int)const" remove="all"/>
+      <add-function signature="__len__" return-type="int">
+          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+                       snippet="qmodelroledataspan-len"/>
+      </add-function>
+      <add-function signature="__getitem__">
+          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+                       snippet="qmodelroledataspan-getitem"/>
+      </add-function>
+  </value-type>
 
-  <object-type name="QAbstractItemModel">
+  <!-- Register meta type for QML properties -->
+  <object-type name="QAbstractItemModel" qt-register-metatype="yes">
     <enum-type name="CheckIndexOption" flags="CheckIndexOptions"/>
     <enum-type name="LayoutChangeHint"/>
      <!-- This function was replaced by a added function -->
     </modify-function>
     <add-function signature="createIndex(int@row@,int@column@,PyObject*@ptr@)const" return-type="QModelIndex">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qabstractitemmodel-createindex"/>
-        <inject-documentation mode="append" format="target">
-            Creates a model index for the given row and column with the internal pointer ptr.
-            When using a QSortFilterProxyModel, its indexes have their own internal pointer.
-            It is not advisable to access this internal pointer outside of the model.
-            Use the data() function instead.
-            This function provides a consistent interface that model subclasses must use to create model indexes.
-
-            .. warning:: Because of some Qt/Python itegration rules, the ptr argument do not get the reference
-                incremented during the QModelIndex life time. So it is necessary to keep the object used
-                on ptr argument alive during the whole process.
-                Do not destroy the object if you are not sure about that.
-        </inject-documentation>
+        <inject-documentation mode="append" format="target"
+                              file="../doc/qtcore.rst" snippet="qabstractitemmodel-createindex"/>
     </add-function>
     <inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="qabstractitemmodel"/>
     <modify-function signature="mimeData(QList&lt;QModelIndex&gt;)const">
         <reference-count action="set"/>
       </modify-argument>
     </modify-function>
-    <!-- FIXME PYSIDE 7: Remove this (QT6_DECL_NEW_OVERLOAD_TAIL) -->
-    <modify-function signature="^moveToThread\(.*\)" remove="all"/>
-    <declare-function signature="moveToThread(QThread*)" return-type="bool"/>
     <modify-function signature="deleteLater()">
       <modify-argument index="this">
         <define-ownership owner="c++"/>
     </modify-function>
     <!-- End of Invalidate-after-use fix -->
     <modify-function signature="parent()const">
-      <modify-argument index="this">
-        <parent index="return" action="add"/>
-      </modify-argument>
       <modify-argument index="return">
         <define-ownership class="target" owner="default"/>
       </modify-argument>
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-6"/>
     </add-function>
 
-    <add-function signature="emit(const char*,...)" return-type="bool">
+    <add-function signature="emit(const char*@signal@,...)" return-type="bool">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-emit"/>
     </add-function>
     <add-function signature="disconnect(const char*@signal@,PyCallable*@functor@)" return-type="bool">
 
     <add-function signature="findChild(PyTypeObject*@type@,const QString&amp;@name@={},Qt::FindChildOptions@options@=Qt::FindChildrenRecursively)"
                   return-type="PyObject*">
-        <inject-documentation format="target" mode="append">
-        To find the child of a certain QObject, the first argument of this function should be the child's type, and the second the name of the child:
-
-        ::
-
-            ...
-            parent = QWidget()
-            ...
-            # The first argument must be the child type
-            child1 = parent.findChild(QPushButton, "child_button")
-            child2 = parent.findChild(QWidget, "child_widget")
-
-        </inject-documentation>
+        <inject-documentation format="target" mode="append"
+                              file="../doc/qtcore.rst" snippet="qobject-findChild"/>
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-findchild-2"/>
-      <modify-argument index="return">
+      <modify-argument index="return" pyi-type="Optional[PlaceHolderType]">
         <parent index="this" action="add"/>
       </modify-argument>
     </add-function>
         Like the method *findChild*, the first parameter should be the child's type.
         </inject-documentation>
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-findchildren"/>
-        <modify-argument index="return">
+        <modify-argument index="return" pyi-type="Iterable[PlaceHolderType]">
             <parent index="this" action="add"/>
         </modify-argument>
     </add-function>
     <add-function signature="findChildren(PyTypeObject*@type@,const QRegularExpression&amp;@pattern@,Qt::FindChildOptions@options@=Qt::FindChildrenRecursively)"
                   return-type="PySequence*" >
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-findchildren"/>
-        <modify-argument index="return">
+        <modify-argument index="return" pyi-type="Iterable[PlaceHolderType]">
             <parent index="this" action="add"/>
         </modify-argument>
     </add-function>
     <extra-includes>
       <include file-name="QStringList" location="global"/>
     </extra-includes>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="'%s'"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="s"/>
     </modify-function>
     <!-- PYSIDE-1499: End of insertion -->
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="s"/>
     <modify-function signature="operator+(QByteArray,const char*)" remove="all"/>
     <modify-function signature="operator+(const char*,QByteArray)" remove="all"/>
     <modify-function signature="operator+(QByteArray,const char*)" remove="all"/>
-    <add-function signature="operator+(PyBytes,QByteArray)">
+    <add-function signature="operator+(PyBytes,QByteArray)" return-type="QByteArray">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorplus-1"/>
     </add-function>
     <add-function signature="operator+(PyByteArray, QByteArray)" return-type="QByteArray">
     <add-function signature="operator+=(PyByteArray)" return-type="QByteArray">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorplusequal"/>
     </add-function>
-    <add-function signature="operator==(PyUnicode)">
+    <add-function signature="operator==(PyUnicode)" return-type="bool">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorequalequal"/>
     </add-function>
-    <add-function signature="operator!=(PyUnicode)">
+    <add-function signature="operator!=(PyUnicode)" return-type="bool">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatornotequal"/>
     </add-function>
-    <add-function signature="operator&gt;(PyUnicode)">
+    <add-function signature="operator&gt;(PyUnicode)" return-type="bool">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorgreater"/>
     </add-function>
-    <add-function signature="operator&gt;=(PyUnicode)">
+    <add-function signature="operator&gt;=(PyUnicode)" return-type="bool">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorgreaterequal"/>
     </add-function>
-    <add-function signature="operator&lt;(PyUnicode)">
+    <add-function signature="operator&lt;(PyUnicode)" return-type="bool">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorlower"/>
     </add-function>
-    <add-function signature="operator&lt;=(PyUnicode)">
+    <add-function signature="operator&lt;=(PyUnicode)" return-type="bool">
         <inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorlowerequal"/>
     </add-function>
     <!-- ### -->
 
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-repr"/>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
                 <replace from="%REDUCE_FORMAT" to="N"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QByteArray(PyByteArray)">
+    <add-function signature="QByteArray(PyByteArray@data@)">
          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-2"/>
     </add-function>
-    <add-function signature="QByteArray(PyBytes)">
+    <add-function signature="QByteArray(PyBytes@data@)">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-3"/>
     </add-function>
     <!-- buffer protocol -->
         <insert-template name="fix_bool*"/>
       </inject-code>
     </modify-function>
-    <add-function signature="__str__" return-type="PyObject*">
+    <add-function signature="__str__" return-type="str">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-str"/>
     </add-function>
-    <add-function signature="__len__">
+    <add-function signature="__len__" return-type="int">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-len"/>
     </add-function>
     <add-function signature="__getitem__">
     <add-function signature="__msetitem__">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-msetitem"/>
     </add-function>
+    <modify-function signature="fromRawData(const char*, qsizetype)">
+      <modify-argument index="1" pyi-type="str"/>
+    </modify-function>
   </value-type>
   <primitive-type name="QByteArrayView" view-on="QByteArray">
     <conversion-rule>
     <object-type name="QIOPipe"/>
     <value-type name="QGenericArgumentHolder"/>
     <value-type name="QGenericReturnArgumentHolder"/>
+    <value-type name="QDirListingIterator">
+        <add-function signature="__iter__()" return-type="PyObject*">
+          <inject-code class="target" position="beginning">
+            <insert-template name="__iter__"/>
+          </inject-code>
+        </add-function>
+        <add-function signature="__next__()" return-type="PyObject*">
+          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+                       snippet="qdirlistingiterator-next"/>
+        </add-function>
+    </value-type>
   </namespace-type>
 
   <!-- Qt5 addition -->
     <configuration condition="QT_CONFIG(thread)"/>
     <modify-function signature="acquire(int)" allow-thread="yes"/>
     <modify-function signature="tryAcquire(int,int)" allow-thread="yes"/>
+    <!-- PYSIDE-2845, clashes with snake-case -->
+    <modify-function signature="try_acquire()" remove="yes"/>
   </object-type>
   <object-type name="QSemaphoreReleaser">
     <configuration condition="QT_CONFIG(thread)"/>
   <object-type name="QTimer">
     <extra-includes>
       <include file-name="pysidestaticstrings.h" location="global"/>
+      <include file-name="qobjectconnect.h" location="global"/>
     </extra-includes>
     <inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
                  snippet="qtimer-singleshot-functorclass"/>
     </extra-includes>
     <add-function signature="QCoreApplication(QStringList@args@)">
         <inject-code file="../glue/qtcore.cpp" snippet="qcoreapplication-1"/>
-        <inject-documentation format="target" mode="append">
-        Constructs a Qt kernel application. Kernel applications are applications
-        without a graphical user interface. These type of applications are used
-        at the console or as server processes.
-
-        The *args* argument is processed by the application, and made available
-        in a more convenient form by the :meth:`~PySide6.QtCore.QCoreApplication.arguments()`
-        method.
-        </inject-documentation>
+        <inject-documentation format="target" mode="append"
+                              file="../doc/qtcore.rst" snippet="qcoreapplication-init"/>
     </add-function>
     <add-function signature="QCoreApplication()">
         <inject-code file="../glue/qtcore.cpp" snippet="qcoreapplication-2"/>
     <modify-function signature="value(QAnyStringView,const QVariant&amp;)const" remove="all"/>
     <add-function signature="value(const QString&amp;, const QVariant&amp; @defaultValue@ = {}, PyObject* @type@ = nullptr)" return-type="PyObject*">
       <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qsettings-value"/>
-      <inject-documentation mode="append" format="target">
-        Custom overload that adds an optional named parameter to the function ``value()``
-        to automatically cast the type that is being returned by the function.
-
-        An example of this situation could be an ini file that contains
-        the value of a one-element list::
-
-            settings.setValue('var', ['a'])
-
-        The the ini file will be::
-
-            [General]
-            var=a  # we cannot know that this is a list!
-
-        Once we read it, we could specify if we want
-        the default behavior, a str, or to cast the output
-        to a list.
-
-            settings.value('var')  # Will get "a"
-            settings.value('var', type=list)  # Will get ["a"]
-      </inject-documentation>
+      <inject-documentation mode="append" format="target"
+                            file="../doc/qtcore.rst" snippet="qsettings-value"/>
     </add-function>
   </object-type>
   <object-type name="QEvent"  polymorphic-id-expression="%1-&gt;type() == QEvent::None"
                qt-register-metatype="base">
     <enum-type name="Type" python-type="IntEnum"/>
-    <add-function signature="__repr__" return-type="PyObject">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning"
                      file="../glue/qtcore.cpp" snippet="repr-qevent"/>
     </add-function>
 
     <!-- ### Replaced by write<TYPE> methods -->
     <modify-function signature="operator&gt;&gt;(qint8&amp;)" remove="all"/>
-    <modify-function signature="operator&gt;&gt;(bool&amp;)" remove="all"/>
     <modify-function signature="operator&gt;&gt;(quint8&amp;)" remove="all"/>
     <modify-function signature="operator&gt;&gt;(qint16&amp;)" remove="all"/>
     <modify-function signature="operator&gt;&gt;(quint16&amp;)" remove="all"/>
 
     <!-- ### Replaced by read<TYPE> methods -->
     <modify-function signature="operator&lt;&lt;(qint8)" remove="all"/>
-    <modify-function signature="operator&lt;&lt;(bool)" remove="all"/>
     <modify-function signature="operator&lt;&lt;(quint8)" remove="all"/>
     <modify-function signature="operator&lt;&lt;(qint16)" remove="all"/>
     <modify-function signature="operator&lt;&lt;(quint16)" remove="all"/>
     <modify-function signature="reserveThread()" allow-thread="yes"/>
     <modify-function signature="setMaxThreadCount(int)" allow-thread="yes"/>
     <modify-function signature="waitForDone(int)" allow-thread="yes"/>
+    <modify-function signature="waitForDone(QDeadlineTimer)" allow-thread="yes"/>
     <modify-function signature="start(QRunnable*,int)" allow-thread="yes">
       <modify-argument index="1">
         <parent index="this" action="add"/>
         <parent index="this" action="add"/>
       </modify-argument>
     </modify-function>
-    <add-function signature="tryStart(PyCallable)" return-type="bool">
+    <add-function signature="tryStart(PyCallable@callable@)" return-type="bool">
        <inject-code class="target" position="beginning"
                     file="../glue/qtcore.cpp"
                     snippet="std-function-void-lambda"/>
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
                      snippet="qmetaobject-invokemethod-return-arg"/>
     </add-function>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qmetaobject-repr"/>
     </add-function>
     <modify-function signature="indexOfClassInfo(const char *)">
     </extra-includes>
     <inject-code file="../glue/qtcore.cpp" snippet="qeasingcurve"/>
     <enum-type name="Type"/>
-    <add-function signature="setCustomType(PyObject*)">
+    <add-function signature="setCustomType(PyObject*@callable@)">
       <inject-code file="../glue/qtcore.cpp" snippet="qeasingcurve-setcustomtype"/>
     </add-function>
     <add-function signature="customType()" return-type="PyObject">
 
   <!-- From Qt4.6 ^^^ -->
 
-  <add-function signature="SIGNAL(const char*)" return-type="str">
+  <add-function signature="SIGNAL(const char*@signature@)" return-type="str">
     <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-signal"/>
     <modify-argument index="1" pyi-type="str"/>
   </add-function>
 
-  <add-function signature="SLOT(const char*)" return-type="str">
+  <add-function signature="SLOT(const char*@signature@)" return-type="str">
     <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-slot"/>
     <modify-argument index="1" pyi-type="str"/>
   </add-function>
 
-  <add-function signature="QT_TR_NOOP(PyObject)" return-type="PyObject*">
+  <add-function signature="QT_TR_NOOP(PyObject@message@)" return-type="PyObject*">
     <inject-code class="target" position="beginning">
       <insert-template name="return_argument"><replace from="#" to="1"/></insert-template>
     </inject-code>
   </add-function>
-  <add-function signature="QT_TR_NOOP_UTF8(PyObject)" return-type="PyObject*">
+  <add-function signature="QT_TR_NOOP_UTF8(PyObject@message@)" return-type="PyObject*">
     <inject-code class="target" position="beginning">
       <insert-template name="return_argument"><replace from="#" to="1"/></insert-template>
     </inject-code>
   </add-function>
-  <add-function signature="QT_TRANSLATE_NOOP(PyObject,PyObject)" return-type="PyObject*">
+  <add-function signature="QT_TRANSLATE_NOOP(PyObject@context@,PyObject@message@)" return-type="PyObject*">
     <inject-code class="target" position="beginning">
       <insert-template name="return_argument"><replace from="#" to="2"/></insert-template>
     </inject-code>
   </add-function>
-  <add-function signature="QT_TRANSLATE_NOOP3(PyObject,PyObject,PyObject)" return-type="PyObject*">
+  <add-function signature="QT_TRANSLATE_NOOP3(PyObject@context@,PyObject@message@,PyObject@disambiguation@)" return-type="PyObject*">
     <inject-code class="target" position="beginning">
       <insert-template name="return_argument"><replace from="#" to="2"/></insert-template>
     </inject-code>
   </add-function>
-  <add-function signature="QT_TRANSLATE_NOOP_UTF8(PyObject)" return-type="PyObject*">
+  <add-function signature="QT_TRANSLATE_NOOP_UTF8(PyObject@message@)" return-type="PyObject*">
     <inject-code class="target" position="beginning">
       <insert-template name="return_argument"><replace from="#" to="1"/></insert-template>
     </inject-code>
 
   <object-type name="QLoggingCategory"/>
 
-  <add-function signature="qCDebug(PyObject *, const char *)">
+  <add-function signature="qCDebug(QLoggingCategory *@logging_category@, const char *@message@)">
     <extra-includes>
     <include file-name="qloggingcategory.h" location="global" />
     </extra-includes>
-    <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
-      qCDebug((*category), "%s", %2);
+    <inject-code class="target" position="beginning">
+      qCDebug(*%1, "%s", %2);
     </inject-code>
   </add-function>
 
-  <add-function signature="qCCritical(PyObject *, const char *)">
-    <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
-      qCCritical((*category), "%s", %2);
+  <add-function signature="qCCritical(QLoggingCategory *@logging_category@, const char *@message@)">
+    <inject-code class="target" position="beginning">
+      qCCritical(*%1, "%s", %2);
     </inject-code>
   </add-function>
 
-  <add-function signature="qCInfo(PyObject *, const char *)">
-    <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
-      qCInfo((*category), "%s", %2);
+  <add-function signature="qCInfo(QLoggingCategory *@logging_category@, const char *@message@)">
+    <inject-code class="target" position="beginning">
+      qCInfo(*%1, "%s", %2);
     </inject-code>
   </add-function>
 
-  <add-function signature="qCWarning(PyObject *, const char *)">
-    <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
-      qCWarning((*category), "%s", %2);
+  <add-function signature="qCWarning(QLoggingCategory *@logging_category@, const char *@message@)">
+    <inject-code class="target" position="beginning">
+      qCWarning(*%1, "%s", %2);
     </inject-code>
   </add-function>
 
index ebed2225773023ce8d7ab16f4f6621701eeec246..c8d7221c44cb2caccf66f6acbdc565f1ba0eaf67 100644 (file)
@@ -86,7 +86,7 @@
       <enum-type name="ErrorType"/>
     </value-type>
     <object-type name="QDBusInterface" qt-metaobject="no">
-      <inject-documentation format="target" mode="append">
+      <inject-documentation format="target" mode="append" emphasis="language-note">
         DBus signals can be captured with string-based connections
         (see :ref:`signals-and-slots-strings`).
       </inject-documentation>
       <enum-type name="WatchModeFlag" flags="WatchMode"/>
     </object-type>
     <object-type name="QDBusSignature"/>
-    <object-type name="QDBusUnixFileDescriptor"/>
+    <value-type name="QDBusUnixFileDescriptor"/>
     <value-type name="QDBusVariant"/>
     <object-type name="QDBusVirtualObject"/>
     <suppress-warning text='^.*Unable to translate type "QDBusReply&lt;.*$'/>
index 70014e3dc26de66535fa0a04683ca870422e1ea3..7d3369adf7273be60032627c5d3129cb95586344 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2017 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtDataVisualization"
+<typesystem package="PySide6.QtDataVisualization" doc-mode="flat"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
 
    <extra-includes>
index 31f8ec15293b70f8d51e3cad4ec74e3c48686b59..19e9742338732696d5bcf6dbe873ce099eaa8aa6 100644 (file)
@@ -114,7 +114,7 @@ QWidget *PyDesignerCustomWidget::createWidget(QWidget *parent)
     }
 
     Shiboken::AutoDecRef pyArgs(PyTuple_New(1));
-    PyTuple_SET_ITEM(pyArgs, 0, pyParent); // tuple will keep pyParent reference
+    PyTuple_SetItem(pyArgs, 0, pyParent); // tuple will keep pyParent reference
 
     // Call python constructor
     auto result = reinterpret_cast<SbkObject *>(PyObject_CallObject(m_pyTypeObject, pyArgs));
index 59cef220b1b9f09b857cb6406ce078fc634b2c3e..b32c39f887850ff47041869e56f2714ef239375a 100644 (file)
@@ -11,6 +11,10 @@ set(QtGraphs_SRC
 # 3D
 ${QtGraphs_GEN_DIR}/qabstract3daxis_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qcategory3daxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qtgraphs3d_wrapper.cpp
+${QtGraphs_GEN_DIR}/qgraphsline_wrapper.cpp
+${QtGraphs_GEN_DIR}/qgraphstheme_wrapper.cpp
+${QtGraphs_GEN_DIR}/qgraphsthemedirtybitfield_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qlogvalue3daxisformatter_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qvalue3daxis_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qvalue3daxisformatter_wrapper.cpp
@@ -26,33 +30,31 @@ ${QtGraphs_GEN_DIR}/qheightmapsurfacedataproxy_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qitemmodelbardataproxy_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qitemmodelscatterdataproxy_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qitemmodelsurfacedataproxy_wrapper.cpp
+${QtGraphs_GEN_DIR}/qlegenddata_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qscatter3dseries_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qscatterdataitem_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qscatterdataproxy_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qsurface3dseries_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qsurfacedataitem_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qsurfacedataproxy_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dbars_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dscatter_wrapper.cpp
 ${QtGraphs_GEN_DIR}/q3dscene_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dsurface_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstract3dgraph_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dinputhandler_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstract3dinputhandler_wrapper.cpp
-${QtGraphs_GEN_DIR}/qtouch3dinputhandler_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dtheme_wrapper.cpp
 # 2D
-${QtGraphs_GEN_DIR}/qbarcategoryaxis_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qabstractaxis_wrapper.cpp
-${QtGraphs_GEN_DIR}/qvalueaxis_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstractbarseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qabstractseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qareaseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qbarcategoryaxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qbarmodelmapper_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qbarseries_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qbarset_wrapper.cpp
+${QtGraphs_GEN_DIR}/qdatetimeaxis_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qlineseries_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstractseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qpiemodelmapper_wrapper.cpp
+${QtGraphs_GEN_DIR}/qpieseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qpieslice_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qscatterseries_wrapper.cpp
-${QtGraphs_GEN_DIR}/qgraphtheme_wrapper.cpp
-${QtGraphs_GEN_DIR}/qseriestheme_wrapper.cpp
+${QtGraphs_GEN_DIR}/qsplineseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qvalueaxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qxymodelmapper_wrapper.cpp
 ${QtGraphs_GEN_DIR}/qxyseries_wrapper.cpp
 # module is always needed
 ${QtGraphs_GEN_DIR}/qtgraphs_module_wrapper.cpp
@@ -63,26 +65,22 @@ set(QtGraphs_include_dirs ${QtGraphs_SOURCE_DIR}
                           ${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
                           ${Qt${QT_MAJOR_VERSION}QtNetwork_INCLUDE_DIRS}
                           ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
-                          ${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
                           ${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
                           ${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
-                          ${Qt${QT_MAJOR_VERSION}QuickWidgets_INCLUDE_DIRS}
                           ${Qt${QT_MAJOR_VERSION}Graphs_INCLUDE_DIRS}
                           ${Qt${QT_MAJOR_VERSION}Quick3D_INCLUDE_DIRS}
                           ${libpyside_SOURCE_DIR}
                           ${QtCore_GEN_DIR}
                           ${QtNetwork_GEN_DIR}
                           ${QtGui_GEN_DIR}
-                          ${QtWidgets_GEN_DIR}
                           ${QtQml_GEN_DIR}
                           ${QtQuick_GEN_DIR}
-                          ${QtQuickWidgets_GEN_DIR}
                           ${QtQuick3D_GEN_DIR})
 
 set(QtGraphs_libraries    pyside6
                           ${Qt${QT_MAJOR_VERSION}Graphs_LIBRARIES})
 
-set(QtGraphs_deps QtCore QtNetwork QtGui QtWidgets QtQml QtQuick QtQuickWidgets QtQuick3D)
+set(QtGraphs_deps QtCore QtNetwork QtGui QtQml QtQuick QtQuick3D)
 
 check_qt_opengl("Graphs" QtGraphs_include_dirs QtGraphs_deps
                 QtGraphs_DROPPED_ENTRIES)
index 43e4458be9f7e61fd293b2354d0adc95a2e89af8..d929bcc0c2cdacd06a07062a4283d09803eb5ee7 100644 (file)
       <include file-name="qtgraphs_helper.h" location="global"/>
    </extra-includes>
 
+  <load-typesystem name="QtQuick/typesystem_quick.xml" generate="no" />
+
   <load-typesystem name="templates/datavisualization_common.xml" generate="no" />
-  <load-typesystem name="QtQuickWidgets/typesystem_quickwidgets.xml" generate="no" />
 
   <function signature="qDefaultSurfaceFormat(bool)"/>
 
+   <namespace-type name="QtGraphs3D">
+     <enum-type name="ElementType"/>
+     <enum-type name="GridLineType"/>
+     <enum-type name="OptimizationHint" flags="OptimizationHints"/>
+     <enum-type name="RenderingMode"/>
+     <enum-type name="SelectionFlag" flags="SelectionFlags"/>
+     <enum-type name="ShadowQuality"/>
+     <enum-type name="CameraPreset" since="6.7"/>
+   </namespace-type>
+
+   <value-type name="QGraphsLine" since="6.8">
+       <modify-function signature="create(QJSValue)" remove="all"/>
+       <modify-function signature="swap(QGraphsLine&amp;)" remove="all"/>
+   </value-type>
 
   <object-type name="QAbstract3DAxis">
     <enum-type name="AxisOrientation"/>
@@ -44,6 +59,7 @@
   <object-type name="QAbstractDataProxy">
     <enum-type name="DataType"/>
   </object-type>
+  <object-type name="QAreaSeries" since="6.8"/>
   <object-type name="QBar3DSeries">
     <modify-function signature="setDataProxy(QBarDataProxy*)">
       <modify-argument index="1">
@@ -53,7 +69,9 @@
   </object-type>
   <value-type name="QBarDataItem"/>
   <object-type name="QBarDataProxy">
+      <enum-type name="RemoveLabels" since="6.8"/>
   </object-type>
+  <object-type name="QBarModelMapper" since="6.8"/>
   <object-type name="QCustom3DItem"/>
   <object-type name="QCustom3DLabel"/>
   <object-type name="QCustom3DVolume">
@@ -63,6 +81,7 @@
                        snippet="qcustom3dvolume-settexturedata"/>
       </add-function>
   </object-type>
+  <object-type name="QDateTimeAxis" since="6.8"/>
   <object-type name="QHeightMapSurfaceDataProxy"/>
   <object-type name="QItemModelBarDataProxy">
     <enum-type name="MultiMatchBehavior"/>
@@ -71,6 +90,7 @@
   <object-type name="QItemModelSurfaceDataProxy">
     <enum-type name="MultiMatchBehavior"/>
   </object-type>
+  <value-type name="QLegendData" since="6.8"/>
   <object-type name="QScatter3DSeries">
     <modify-function signature="setDataProxy(QScatterDataProxy*)">
       <modify-argument index="1">
       </modify-argument>
     </modify-function>
   </object-type>
+  <object-type name="QPieModelMapper" since="6.8"/>
+  <object-type name="QPieSeries" since="6.8"/>
+  <object-type name="QPieSlice" since="6.8">
+      <enum-type name="LabelPosition"/>
+  </object-type>
   <value-type name="QScatterDataItem"/>
   <object-type name="QScatterDataProxy">
   </object-type>
+  <object-type name="QSplineSeries" since="6.8"/>
   <object-type name="QSurface3DSeries">
+    <enum-type name="Shading" since="6.8"/>
     <enum-type name="DrawFlag" flags="DrawFlags"/>
     <modify-function signature="QSurface3DSeries(QSurfaceDataProxy*,QObject*)">
       <modify-argument index="1">
         ``deltaZ``, respectively.
         </inject-documentation>
     </add-function>
-
-  </object-type>
-  <object-type name="Q3DBars">
-    <modify-function signature="addAxis(QAbstract3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="releaseAxis(QAbstract3DAxis*)">
-      <modify-argument index="1">
-        <reference-count action="set"/>
-      </modify-argument>
-      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
-    </modify-function>
-    <modify-function signature="setColumnAxis(QCategory3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="setRowAxis(QCategory3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-  </object-type>
-  <object-type name="Q3DScatter">
-    <modify-function signature="addAxis(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="releaseAxis(QValue3DAxis*)">
-      <modify-argument index="1">
-        <reference-count action="set"/>
-      </modify-argument>
-      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
-    </modify-function>
-    <modify-function signature="setAxisX(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="setAxisY(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="setAxisZ(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
   </object-type>
   <object-type name="Q3DScene"/>
-  <object-type name="Q3DSurface">
-    <modify-function signature="addAxis(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="releaseAxis(QValue3DAxis*)">
-      <modify-argument index="1">
-        <reference-count action="set"/>
-      </modify-argument>
-      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
-    </modify-function>
-    <modify-function signature="setAxisX(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="setAxisY(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="setAxisZ(QValue3DAxis*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-  </object-type>
-  <object-type name="QAbstract3DGraph">
-    <enum-type name="ElementType"/>
-    <enum-type name="OptimizationHint" flags="OptimizationHints"/>
-    <enum-type name="RenderingMode"/>
-    <enum-type name="SelectionFlag" flags="SelectionFlags"/>
-    <enum-type name="ShadowQuality"/>
-    <enum-type name="CameraPreset" since="6.7"/>
-    <modify-function signature="addCustomItem(QCustom3DItem*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="addInputHandler(QAbstract3DInputHandler*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="addTheme(Q3DTheme*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="releaseCustomItem(QCustom3DItem*)">
-      <modify-argument index="1">
-        <reference-count action="set"/>
-      </modify-argument>
-      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
-    </modify-function>
-    <modify-function signature="releaseInputHandler(QAbstract3DInputHandler*)">
-      <modify-argument index="1">
-        <reference-count action="set"/>
-      </modify-argument>
-      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
-    </modify-function>
-    <modify-function signature="releaseTheme(Q3DTheme*)">
-      <modify-argument index="1">
-        <reference-count action="set"/>
-      </modify-argument>
-      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
-    </modify-function>
-    <modify-function signature="setActiveInputHandler(QAbstract3DInputHandler*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-    <modify-function signature="setActiveTheme(Q3DTheme*)">
-      <modify-argument index="1">
-        <parent index="this" action="add"/>
-      </modify-argument>
-    </modify-function>
-  </object-type>
-  <object-type name="Q3DInputHandler"/>
-  <object-type name="QAbstract3DInputHandler">
-    <enum-type name="InputView"/>
-  </object-type>
-  <object-type name="QTouch3DInputHandler"/>
-  <object-type name="Q3DTheme">
+  <object-type name="QGraphsTheme" since="6.8">
+    <enum-type name="ColorScheme"/>
     <enum-type name="ColorStyle"/>
+    <enum-type name="ForceTheme"/>
     <enum-type name="Theme"/>
   </object-type>
+  <value-type name="QGraphsThemeDirtyBitField" since="6.8"/>
 
   <!-- 2D -->
   <object-type name="QBarCategoryAxis"/>
     <enum-type name="AxisType"/>
   </object-type>
   <object-type name="QValueAxis"/>
-  <object-type name="QAbstractBarSeries">
+  <object-type name="QBarSeries">
     <enum-type name="LabelsPosition"/>
+    <enum-type name="BarsType" since="6.8"/>
     <modify-function signature="append(QBarSet*)">
       <modify-argument index="1">
         <parent index="this" action="add"/>
         <parent index="this" action="add"/>
       </modify-argument>
     </modify-function>
-    <modify-function signature="insert(int,QBarSet*)">
+    <modify-function signature="insert(qsizetype,QBarSet*)">
       <modify-argument index="2">
         <parent index="this" action="add"/>
       </modify-argument>
       </modify-argument>
     </modify-function>
   </object-type>
-  <object-type name="QBarSeries"/>
   <object-type name="QBarSet"/>
   <object-type name="QLineSeries"/>
   <object-type name="QAbstractSeries">
     <enum-type name="SeriesType"/>
   </object-type>
   <object-type name="QScatterSeries"/>
-  <object-type name="QGraphTheme">
-    <enum-type name="ColorTheme"/>
-  </object-type>
-  <object-type name="QSeriesTheme">
-    <enum-type name="SeriesColorTheme"/>
-  </object-type>
+  <object-type name="QXYModelMapper" since="6.8"/>
   <object-type name="QXYSeries">
+      <extra-includes>
+          <include file-name="pyside_numpy.h" location="global"/>
+      </extra-includes>
+      <add-function signature="appendNp(PyArrayObject *@x@, PyArrayObject *@y@)">
+          <inject-code file="../glue/qtcharts.cpp" snippet="qxyseries-appendnp-numpy-x-y"/>
+          <inject-documentation format="target" mode="append">
+          Adds the list of data points specified by two
+          one-dimensional, equally sized numpy arrays representing the x, y values, respectively.
+          </inject-documentation>
+      </add-function>
+      <add-function signature="replaceNp(PyArrayObject *@x@, PyArrayObject *@y@)">
+          <inject-code file="../glue/qtcharts.cpp" snippet="qxyseries-replacenp-numpy-x-y"/>
+          <inject-documentation format="target" mode="append">
+          Replaces the current points with the points specified by two
+          one-dimensional, equally sized numpy arrays representing the x, y values, respectively.
+          </inject-documentation>
+      </add-function>
   </object-type>
 
   <extra-includes>
diff --git a/sources/pyside6/PySide6/QtGraphsWidgets/CMakeLists.txt b/sources/pyside6/PySide6/QtGraphsWidgets/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d434d60
--- /dev/null
@@ -0,0 +1,54 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+project(QtGraphsWidgets)
+
+set (QtGraphsWidgets_DROPPED_ENTRIES)
+
+set(QtGraphsWidgets_SRC
+${QtGraphsWidgets_GEN_DIR}/q3dbarswidgetitem_wrapper.cpp
+${QtGraphsWidgets_GEN_DIR}/q3dgraphswidgetitem_wrapper.cpp
+${QtGraphsWidgets_GEN_DIR}/q3dscatterwidgetitem_wrapper.cpp
+${QtGraphsWidgets_GEN_DIR}/q3dsurfacewidgetitem_wrapper.cpp
+
+# module is always needed
+${QtGraphsWidgets_GEN_DIR}/qtgraphswidgets_module_wrapper.cpp
+)
+
+set(QtGraphsWidgets_include_dirs ${QtGraphsWidgets_SOURCE_DIR}
+                                 ${QtGraphsWidgets_BINARY_DIR}
+                                 ${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
+                                 ${Qt${QT_MAJOR_VERSION}QtNetwork_INCLUDE_DIRS}
+                                 ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+                                 ${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
+                                 ${Qt${QT_MAJOR_VERSION}Graphs_INCLUDE_DIRS}
+                                 ${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
+                                 ${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
+                                 ${Qt${QT_MAJOR_VERSION}QuickWidgets_INCLUDE_DIRS}
+                                 ${QtCore_GEN_DIR}
+                                 ${QtGui_GEN_DIR}
+                                 ${QtNetwork_GEN_DIR}
+                                 ${QtWidgets_GEN_DIR}
+                                 ${QtGraphs_GEN_DIR}
+                                 ${QtQml_GEN_DIR}
+                                 ${QtQuick_GEN_DIR}
+                                 ${QtQuickWidgets_GEN_DIR}
+                                 ${libpyside_SOURCE_DIR}
+)
+
+set(QtGraphsWidgets_libraries pyside6
+    ${Qt${QT_MAJOR_VERSION}GraphsWidgets_LIBRARIES})
+
+set(QtGraphsWidgets_deps QtQuickWidgets QtGraphs)
+
+check_qt_opengl("GraphsWidgets" QtGraphsWidgets_include_dirs QtGraphsWidgets_deps
+                 QtGraphsWidgets_DROPPED_ENTRIES)
+
+create_pyside_module(NAME QtGraphsWidgets
+                     INCLUDE_DIRS QtGraphsWidgets_include_dirs
+                     LIBRARIES QtGraphsWidgets_libraries
+                     DEPS QtGraphsWidgets_deps
+                     TYPESYSTEM_PATH QtGraphsWidgets_SOURCE_DIR
+                     SOURCES QtGraphsWidgets_SRC
+                     STATIC_SOURCES QtGraphsWidgets_src
+                     DROPPED_ENTRIES QtGraphsWidgets_DROPPED_ENTRIES)
diff --git a/sources/pyside6/PySide6/QtGraphsWidgets/typesystem_graphswidgets.xml b/sources/pyside6/PySide6/QtGraphsWidgets/typesystem_graphswidgets.xml
new file mode 100644 (file)
index 0000000..806903a
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+-->
+<typesystem package="PySide6.QtGraphsWidgets" doc-package="PySide6.QtGraphs"
+            namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
+
+  <load-typesystem name="QtQuickWidgets/typesystem_quickwidgets.xml" generate="no" />
+  <load-typesystem name="QtGraphs/typesystem_graphs.xml" generate="no" />
+
+  <object-type name="Q3DBarsWidgetItem">
+    <modify-function signature="addAxis(QAbstract3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="releaseAxis(QAbstract3DAxis*)">
+      <modify-argument index="1">
+        <reference-count action="set"/>
+      </modify-argument>
+      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+    </modify-function>
+    <modify-function signature="setColumnAxis(QCategory3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="setRowAxis(QCategory3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+  </object-type>
+
+  <object-type name="Q3DScatterWidgetItem">
+    <modify-function signature="addAxis(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="releaseAxis(QValue3DAxis*)">
+      <modify-argument index="1">
+        <reference-count action="set"/>
+      </modify-argument>
+      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+    </modify-function>
+    <modify-function signature="setAxisX(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="setAxisY(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="setAxisZ(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+  </object-type>
+
+  <object-type name="Q3DSurfaceWidgetItem">
+    <modify-function signature="addAxis(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="releaseAxis(QValue3DAxis*)">
+      <modify-argument index="1">
+        <reference-count action="set"/>
+      </modify-argument>
+      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+    </modify-function>
+    <modify-function signature="setAxisX(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="setAxisY(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="setAxisZ(QValue3DAxis*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+  </object-type>
+
+  <object-type name="Q3DGraphsWidgetItem">
+    <modify-function signature="addCustomItem(QCustom3DItem*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="addTheme(QGraphsTheme*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="releaseCustomItem(QCustom3DItem*)">
+      <modify-argument index="1">
+        <reference-count action="set"/>
+      </modify-argument>
+      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+    </modify-function>
+    <modify-function signature="releaseTheme(QGraphsTheme*)">
+      <modify-argument index="1">
+        <reference-count action="set"/>
+      </modify-argument>
+      <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+    </modify-function>
+    <modify-function signature="setActiveTheme(QGraphsTheme*)">
+      <modify-argument index="1">
+        <parent index="this" action="add"/>
+      </modify-argument>
+    </modify-function>
+  </object-type>
+
+</typesystem>
index 99e0789d1326364975de25e6dac7c9d1404d29c7..9400753860fa600b3b1d578b914673c973032a41 100644 (file)
@@ -57,6 +57,7 @@ ${QtGui_GEN_DIR}/qrhitexturerendertargetdescription_wrapper.cpp
 ${QtGui_GEN_DIR}/qrhitexturesubresourceuploaddescription_wrapper.cpp
 ${QtGui_GEN_DIR}/qrhitextureuploaddescription_wrapper.cpp
 ${QtGui_GEN_DIR}/qrhitextureuploadentry_wrapper.cpp
+${QtGui_GEN_DIR}/qrhitexture_viewformat_wrapper.cpp
 ${QtGui_GEN_DIR}/qrhivertexinputattribute_wrapper.cpp
 ${QtGui_GEN_DIR}/qrhivertexinputbinding_wrapper.cpp
 ${QtGui_GEN_DIR}/qrhivertexinputlayout_wrapper.cpp
@@ -85,6 +86,8 @@ ${QtGui_GEN_DIR}/qabstracttextdocumentlayout_selection_wrapper.cpp
 ${QtGui_GEN_DIR}/qabstracttextdocumentlayout_wrapper.cpp
 ${QtGui_GEN_DIR}/qaccessible_wrapper.cpp
 ${QtGui_GEN_DIR}/qaccessibleactioninterface_wrapper.cpp
+${QtGui_GEN_DIR}/qaccessibleannouncementevent_wrapper.cpp
+${QtGui_GEN_DIR}/qaccessibleattributesinterface_wrapper.cpp
 ${QtGui_GEN_DIR}/qaccessibleeditabletextinterface_wrapper.cpp
 ${QtGui_GEN_DIR}/qaccessibleevent_wrapper.cpp
 ${QtGui_GEN_DIR}/qaccessibleobject_wrapper.cpp
@@ -190,6 +193,7 @@ ${QtGui_GEN_DIR}/qpainterpath_wrapper.cpp
 ${QtGui_GEN_DIR}/qpainterpathstroker_wrapper.cpp
 ${QtGui_GEN_DIR}/qpaintevent_wrapper.cpp
 ${QtGui_GEN_DIR}/qpalette_wrapper.cpp
+${QtGui_GEN_DIR}/qpdfoutputintent_wrapper.cpp
 ${QtGui_GEN_DIR}/qpdfwriter_wrapper.cpp
 ${QtGui_GEN_DIR}/qpen_wrapper.cpp
 ${QtGui_GEN_DIR}/qpicture_wrapper.cpp
@@ -313,6 +317,15 @@ set(QtGui_private_include_dirs
     ${Qt${QT_MAJOR_VERSION}Core_PRIVATE_INCLUDE_DIRS}
     ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
 
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Android")
+    if (QT_FEATURE_opengles2)
+        # add openGL ES 2.0
+        find_package(GLESv2 REQUIRED)
+    else()
+        message(FATAL_ERROR "QtGui requires OpenGL ES 2.0 on Android")
+    endif()
+endif()
+
 configure_file("${QtGui_SOURCE_DIR}/QtGui_global.post.h.in"
                "${QtGui_BINARY_DIR}/QtGui_global.post.h" @ONLY)
 
@@ -343,4 +356,3 @@ create_pyside_module(NAME QtGui
 install(FILES ${pyside6_SOURCE_DIR}/qpytextobject.h
               ${pyside6_SOURCE_DIR}/qtguihelper.h
         DESTINATION include/PySide6/QtGui/)
-
index 0cab80a149acd490494b5571ea5e13d817e82894..e5637cf23e474e9f09177ae5451e27a5539f11a6 100644 (file)
@@ -81,7 +81,8 @@
   <!-- For Qt::mightBeRichText(QString), Qt::convertFromPlainText(QString,Qt::WhiteSpaceMode)
        Match on files from '/QtGui/' (shadow build) or '/gui/' (developer build)
        or '/QtGui.framework' (macOS) -->
-  <namespace-type name="Qt" files="^.*/(gui|QtGui)[/.].*\.h$" extends="PySide6.QtCore"/>
+  <namespace-type name="Qt" files="^.*/(gui|QtGui)[/.].*\.h$" extends="PySide6.QtCore"
+                  doc-file="qt-sub-qtgui"/>
 
   <primitive-type name="WId" target-lang-api-name="PyLong">
     <conversion-rule>
                    file="../glue/qtgui.cpp"
                    snippet="qaccessible-pysidefactory"/>
       <value-type name="State"/>
+      <enum-type name="AnnouncementPoliteness" since="6.8"/>
       <enum-type name="Event"/>
       <enum-type name="Role"/>
       <enum-type name="Text"/>
       <enum-type name="RelationFlag" flags="Relation"/>
       <enum-type name="InterfaceType"/>
       <enum-type name="TextBoundaryType"/>
+      <enum-type name="Attribute" since="6.8"/>
       <add-function signature="installFactory(PyCallable)" static="yes">
           <inject-code class="target" position="beginning"
                        file="../glue/qtgui.cpp"
   </value-type>
 
   <object-type name="QAccessibleActionInterface"/>
+  <object-type name="QAccessibleAttributesInterface" since="6.8"/>
   <object-type name="QAccessibleEditableTextInterface"/>
   <object-type name="QAccessibleInterface"/>
   <object-type name="QAccessibleObject" qt-register-metatype="base"/>
 
   <object-type name="QAccessibleEvent"
                polymorphic-id-expression="%B-&gt;type() == QAccessible::InvalidEvent"/>
+  <object-type name="QAccessibleAnnouncementEvent"
+               polymorphic-id-expression="%B-&gt;type() == QAccessible::Announcement"/>
   <object-type name="QAccessibleStateChangeEvent"
                polymorphic-id-expression="%B-&gt;type() == QAccessible::StateChanged"/>
   <object-type name="QAccessibleTableModelChangeEvent"
     </modify-function>
   </object-type>
   <object-type name="QShortcut">
-    <add-function signature="QShortcut(QKeySequence&amp;,QObject*,PyCallable*,Qt::ShortcutContext=Qt::WindowShortcut)">
+    <add-function signature="QShortcut(QKeySequence&amp;@key@,QObject*@parent@,PyCallable*@callable@,Qt::ShortcutContext@context@=Qt::WindowShortcut)">
       <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qshortcut-1"/>
       <inject-code class="target" position="end" file="../glue/qtgui.cpp" snippet="qshortcut-2"/>
     </add-function>
-    <add-function signature="QShortcut(QKeySequence::StandardKey,QObject*,PyCallable*,Qt::ShortcutContext=Qt::WindowShortcut)">
+    <add-function signature="QShortcut(QKeySequence::StandardKey@standard_key@,QObject*@parent@,PyCallable*@callable@,Qt::ShortcutContext@context@=Qt::WindowShortcut)">
       <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qshortcut-1"/>
       <inject-code class="target" position="end" file="../glue/qtgui.cpp" snippet="qshortcut-2"/>
     </add-function>
 
   <value-type name="QTransform">
     <enum-type name="TransformationType"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f, %f, %f, %f, %f, %f, %f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="ddddddddd"/>
         <insert-template name="fix_bool*"/>
       </inject-code>
     </modify-function>
-    <add-function signature="quadToQuad(QPolygonF&amp;,QPolygonF&amp;)" return-type="PyObject*" static="true">
+    <add-function signature="quadToQuad(QPolygonF&amp;@one@,QPolygonF&amp;@two@)" return-type="PyObject*" static="true">
       <inject-code file="../glue/qtgui.cpp" snippet="qtransform-quadtoquad"/>
     </add-function>
     <add-function signature="quadToSquare(QPolygonF &amp;)" return-type="PyObject*" static="true">
     <enum-type name="SequenceMatch"/>
     <enum-type name="StandardKey"/>
 
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qkeysequence-repr"/>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="iiii"/>
   </value-type>
   <value-type name="QRegion" >
     <enum-type name="RegionType"/>
-    <add-function signature="__len__">
+    <add-function signature="__len__" return-type="int">
         <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qregion-len"/>
     </add-function>
     <add-function signature="__getitem__">
     <add-function signature="operator==(const QPolygon&amp;)" return-type="bool"/>
     <add-function signature="operator!=(const QPolygon&amp;)" return-type="bool"/>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qpolygon-reduce">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="N"/>
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qfile-path-1"/>
     </modify-function>
 
-    <add-function signature="QImage(QString&amp;,int,int,int,QImage::Format)">
+    <add-function signature="QImage(QString&amp;,int@width@,int@height@,int@bytes_per_line@,QImage::Format@format@)">
         <inject-code>
             <insert-template name="qimage_buffer_constructor">
                 <replace from="%ARGS" to="%2, %3, %4, %5"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QImage(QString&amp;,int,int,QImage::Format)">
+    <add-function signature="QImage(QString&amp;,int@width@,int@height@,QImage::Format@format@)">
         <inject-code>
             <insert-template name="qimage_buffer_constructor">
                 <replace from="%ARGS" to="%2, %3, %4"/>
     <extra-includes>
       <include file-name="QVariant" location="global"/>
     </extra-includes>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="qcolor_repr"/>
         </inject-code>
     </add-function>
-    <add-function signature="__str__" return-type="PyObject*">
+    <add-function signature="__str__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="qcolor_repr"/>
         </inject-code>
     <add-function signature="__setstate__(PyObject*)" return-type="PyObject">
         <inject-code file="../glue/qtgui.cpp" snippet="qcolor-setstate"/>
     </add-function>
-    <add-function signature="__reduce__" return-type="PyObject">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qcolor-reduce"/>
     </add-function>
 
     <!-- ### Constructor removed because we already have an overload using QString. -->
     <modify-function signature="QColor(const char*)" remove="all"/>
     <!-- ### -->
-    <add-function signature="QColor(QVariant)">
+    <add-function signature="QColor(QVariant@color@)">
         <inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qcolor"/>
     </add-function>
     <!-- get* methods. Inject code -->
   </namespace-type>
 
   <value-type name="QColorSpace">
+      <enum-type name="ColorModel" since="6.8"/>
       <enum-type name="NamedColorSpace"/>
       <enum-type name="Primaries"/>
       <enum-type name="TransferFunction"/>
+      <enum-type name="TransformModel" since="6.8"/>
   </value-type>
 
   <value-type name="QColorTransform"/>
   <value-type name="QLinearGradient" polymorphic-id-expression="%B-&gt;type() == QGradient::LinearGradient"/>
   <object-type name="QPaintDevice">
       <enum-type name="PaintDeviceMetric"/>
+      <!-- inline error -->
+      <modify-function signature="getDecodedMetricF(QPaintDevice::PaintDeviceMetric,QPaintDevice::PaintDeviceMetric)const" remove="all"/>
   </object-type>
   <object-type name="QPagedPaintDevice">
     <enum-type name="PdfVersion"/>
   <object-type name="QPainterPathStroker" copyable="false"/>
   <object-type name="QPixmapCache">
     <value-type name="Key"/>
-    <add-function signature="find(QPixmapCache::Key&amp;)">
+    <add-function signature="find(QPixmapCache::Key&amp;@key@)">
         <inject-code file="../glue/qtgui.cpp" snippet="qpixmapcache-find"/>
     </add-function>
-    <add-function signature="find(const QString&amp;)">
+    <add-function signature="find(const QString&amp;@key@)">
         <inject-code file="../glue/qtgui.cpp" snippet="qpixmapcache-find"/>
     </add-function>
   </object-type>
       <enum-type name="State"/>
   </value-type>
   <object-type name="QDragEnterEvent" polymorphic-id-expression="%B-&gt;type() == QEvent::DragEnter">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
       </add-function>
   </object-type>
   <object-type name="QDragLeaveEvent" polymorphic-id-expression="%B-&gt;type() == QEvent::DragLeave">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
       </add-function>
   </object-type>
   <object-type name="QDragMoveEvent" polymorphic-id-expression="%B-&gt;type() == QEvent::DragMove">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
         <define-ownership class="target" owner="default"/>
       </modify-argument>
     </modify-function>
-    <add-function signature="__repr__" return-type="PyObject">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_qdebug_gui"/>
         </inject-code>
   <object-type name="QFileOpenEvent" polymorphic-id-expression="%B-&gt;type() == QEvent::FileOpen"/>
   <object-type name="QFocusEvent"
                polymorphic-id-expression="%B-&gt;type() == QEvent::FocusIn || %B-&gt;type() == QEvent::FocusOut">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
     <value-type name="Attribute">
       <include file-name="QInputMethodEvent" location="global"/>
     </value-type>
-    <add-function signature="__repr__" return-type="PyObject">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_qdebug_gui"/>
         </inject-code>
                polymorphic-id-expression="%B-&gt;type() == QEvent::InputMethodQuery"/>
 
   <object-type name="QMoveEvent" polymorphic-id-expression="%B-&gt;type() == QEvent::Move">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
      <enum-type name="SurfaceEventType"/>
   </object-type>
   <object-type name="QResizeEvent" polymorphic-id-expression="%B-&gt;type() == QEvent::Resize">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
                polymorphic-id-expression="%B-&gt;type() == QEvent::StatusTip"/>
   <object-type name="QTabletEvent"
                polymorphic-id-expression="%B-&gt;type() == QEvent::TabletMove || %B-&gt;type() == QEvent::TabletPress || %B-&gt;type() == QEvent::TabletRelease">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
   <object-type name="QWhatsThisClickedEvent"
                polymorphic-id-expression="%B-&gt;type() == QEvent::WhatsThisClicked"/>
   <object-type name="QWheelEvent" polymorphic-id-expression="%B-&gt;type() == QEvent::Wheel">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
 
   <object-type name="QWindowStateChangeEvent"
                polymorphic-id-expression="%B-&gt;type() == QEvent::WindowStateChange">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
   <object-type name="QInputEvent"/>
   <object-type name="QKeyEvent"
                polymorphic-id-expression="%B-&gt;type() == QEvent::KeyPress || %B-&gt;type() == QEvent::KeyRelease || %B-&gt;type() == QEvent::ShortcutOverride">
-      <add-function signature="operator!=(QKeySequence::StandardKey)">
+      <add-function signature="operator!=(QKeySequence::StandardKey@standard_key@)">
           <inject-code class="target" file="../glue/qtgui.cpp" snippet="qkeyevent-operatornotequal"/>
       </add-function>
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
       <modify-function signature="y() const" deprecated="yes"/>
       <modify-function signature="globalX() const" deprecated="yes"/>
       <modify-function signature="globalY() const" deprecated="yes"/>
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
       <enum-type name="ScrollState"/>
   </object-type>
   <object-type name="QPointerEvent" copyable= "false">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
 
 
     <modify-function signature="drawConvexPolygon(const QPoint*,int)" remove="all"/>
-    <add-function signature="drawConvexPolygon(QList&lt;QPoint>)">
+    <add-function signature="drawConvexPolygon(QList&lt;QPoint>@points@)">
         <inject-code>
             <insert-template name="qpainter_drawlist"/>
         </inject-code>
     </add-function>
     <modify-function signature="drawConvexPolygon(const QPointF*,int)" remove="all"/>
-    <add-function signature="drawConvexPolygon(QList&lt;QPointF>)">
+    <add-function signature="drawConvexPolygon(QList&lt;QPointF>@points@)">
         <inject-code>
             <insert-template name="qpainter_drawlist"/>
         </inject-code>
     <!-- PYSIDE-1540: Preferably use the QPolygon overloads first to avoid
          a costly sequence type check on QPolygon. -->
     <modify-function signature="drawPoints(const QPoint*,int)" remove="all"/>
-    <add-function signature="drawPoints(QList&lt;QPoint>)" overload-number="2">
+    <add-function signature="drawPoints(QList&lt;QPoint>@points@)" overload-number="2">
         <inject-code>
             <insert-template name="qpainter_drawlist"/>
         </inject-code>
     </add-function>
-    <add-function signature="drawPoints(QList&lt;QPointF>)" overload-number="3">
+    <add-function signature="drawPoints(QList&lt;QPointF>@points@)" overload-number="3">
         <inject-code>
             <insert-template name="qpainter_drawlist"/>
         </inject-code>
     </add-function>
 
     <modify-function signature="drawPolygon(const QPoint*,int,Qt::FillRule)" remove="all"/>
-    <add-function signature="drawPolygon(QList&lt;QPoint>,Qt::FillRule)">
+    <add-function signature="drawPolygon(QList&lt;QPoint>@points@,Qt::FillRule@fill_rule@)">
         <inject-code file="../glue/qtgui.cpp" snippet="qpainter-drawpolygon"/>
     </add-function>
     <modify-function signature="drawPolygon(const QPointF*,int,Qt::FillRule)" remove="all"/>
-    <add-function signature="drawPolygon(QList&lt;QPointF>,Qt::FillRule)">
+    <add-function signature="drawPolygon(QList&lt;QPointF>@points@,Qt::FillRule@fill_rule@)">
         <inject-code file="../glue/qtgui.cpp" snippet="qpainter-drawpolygon"/>
     </add-function>
     <modify-function signature="drawPolyline(const QPoint*,int)" remove="all"/>
-    <add-function signature="drawPolyline(QList&lt;QPoint>)">
+    <add-function signature="drawPolyline(QList&lt;QPoint>@points@)">
         <inject-code>
             <insert-template name="qpainter_drawlist"/>
         </inject-code>
     <modify-function signature="drawPolyline(const QPointF*,int)" remove="all"/>
     <modify-function signature="drawPolyline(const QPolygon&amp;)" overload-number="0"/>
     <modify-function signature="drawPolyline(const QPolygonF&amp;)" overload-number="1"/>
-    <add-function signature="drawPolyline(QList&lt;QPointF>)">
+    <add-function signature="drawPolyline(QList&lt;QPointF@points@>)">
         <inject-code>
             <insert-template name="qpainter_drawlist"/>
         </inject-code>
     <modify-function signature="QMatrix2x2(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="4"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix2x2(PySequence*)">
+    <add-function signature="QMatrix2x2(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="4"/>
     <modify-function signature="QMatrix2x3(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="6"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix2x3(PySequence*)">
+    <add-function signature="QMatrix2x3(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="6"/>
     <modify-function signature="QMatrix2x4(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="8"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix2x4(PySequence*)">
+    <add-function signature="QMatrix2x4(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="8"/>
     <modify-function signature="QMatrix3x2(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="6"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix3x2(PySequence*)">
+    <add-function signature="QMatrix3x2(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="6"/>
     <modify-function signature="QMatrix3x3(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="9"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix3x3(PySequence*)">
+    <add-function signature="QMatrix3x3(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="9"/>
     <modify-function signature="QMatrix3x4(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="12"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix3x4(PySequence*)">
+    <add-function signature="QMatrix3x4(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="12"/>
     <modify-function signature="QMatrix4x2(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="8"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix4x2(PySequence*)">
+    <add-function signature="QMatrix4x2(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="8"/>
     <modify-function signature="QMatrix4x3(const float*)" remove="all"/>
     <modify-function signature="copyDataTo(float*) const" remove="all"/>
     <modify-function signature="constData()const" remove="all"/>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="12"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
             </insert-template>
         </inject-code>
     </add-function>
-    <add-function signature="QMatrix4x3(PySequence*)">
+    <add-function signature="QMatrix4x3(PySequence*@elements@)">
       <inject-code class="target" position="beginning">
         <insert-template name="matrix_constructor">
           <replace from="%SIZE" to="12"/>
     <modify-function signature="__dummy(const QList&lt;float &gt; &amp;)" remove="all"/>
     <!-- that was the trick ^^^^^^^^^^^^^^^^^^^^^^ -->
 
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code_matrix">
                <replace from="%MATRIX_SIZE" to="16"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code_matrix">
               <replace from="%MATRIX_TYPE" to="float"/>
     _______ end of matrix block _______ -->
 
   <value-type name="QQuaternion" since="4.6">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="dddd"/>
   </value-type>
 
   <object-type name="QTouchEvent" since="4.6">
-      <add-function signature="__repr__" return-type="PyObject">
+      <add-function signature="__repr__" return-type="str">
           <inject-code class="target" position="beginning">
               <insert-template name="repr_qdebug_gui"/>
           </inject-code>
   </object-type>
 
   <value-type name="QVector2D" since="4.6">
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="dd"/>
     <extra-includes>
       <include file-name="QMatrix4x4" location="global"/>
     </extra-includes>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="ddd"/>
     <extra-includes>
       <include file-name="QMatrix4x4" location="global"/>
     </extra-includes>
-    <add-function signature="__repr__" return-type="PyObject*">
+    <add-function signature="__repr__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="repr_code">
                <replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
         </inject-code>
     </add-function>
 
-    <add-function signature="__reduce__" return-type="PyObject*">
+    <add-function signature="__reduce__" return-type="str">
         <inject-code class="target" position="beginning">
             <insert-template name="reduce_code">
               <replace from="%REDUCE_FORMAT" to="dddd"/>
     </extra-includes>
   </primitive-type>
 
-  <object-type name="QWindow" delete-in-main-thread="true">
+  <object-type name="QWindow" delete-in-main-thread="true" polymorphic-base="true">
     <enum-type name="AncestorMode"/>
     <enum-type name="Visibility"/>
     <modify-function signature="raise()" rename="raise_"/>
       <include file-name="qtguihelper.h" location="local"/>
     </extra-includes>
     <modify-function signature="QGuiApplication(int&amp;,char**,int)" access="private"/>
-    <add-function signature="QGuiApplication(QStringList)">
+    <add-function signature="QGuiApplication(QStringList@arguments@)">
         <inject-code file="../glue/qtgui.cpp" snippet="qguiapplication-1"/>
     </add-function>
     <add-function signature="QGuiApplication()">
   <value-type name="QPageLayout">
       <enum-type name="Mode"/>
       <enum-type name="Orientation"/>
+      <enum-type name="OutOfBoundsPolicy" since="6.8"/>
       <enum-type name="Unit"/>
   </value-type>
   <value-type name="QPageRanges">
       <enum-type name="TypeInterpretation"/>
       <enum-type name="YUVLayout"/>
   </value-type>
-  <object-type name="QPdfWriter"/>
+  <value-type name="QPdfOutputIntent"/>
+  <object-type name="QPdfWriter">
+      <enum-type name="ColorModel" since="6.8"/>
+  </object-type>
   <object-type name="QPointingDevice">
     <enum-type name="PointerType" flags="PointerTypes"/>
     <enum-type name="GrabTransition"/>
index 22ab56e20f51c0fd6027b0fb21ee0064bcf9ddcb..656f18ca4e26ceba7b90c83fd2c5a30bd6243dd7 100644 (file)
@@ -65,6 +65,7 @@
   <object-type name="QRhiTexture" since="6.6" private="yes">
       <enum-type name="Format"/>
       <enum-type name="Flag" flags="Flags"/>
+      <value-type name="ViewFormat" private="yes" since="6.8"/>
   </object-type>
   <object-type name="QRhiSampler" since="6.6" private="yes">
       <enum-type name="Filter"/>
index 7bf46dcefaaeb2bc437f25748c799d69e577443b..46509e70391d195aba93a50f3883d47f3df3144b 100644 (file)
@@ -18,6 +18,7 @@ ${QtHelp_GEN_DIR}/qhelpindexmodel_wrapper.cpp
 ${QtHelp_GEN_DIR}/qhelpindexwidget_wrapper.cpp
 ${QtHelp_GEN_DIR}/qhelplink_wrapper.cpp
 ${QtHelp_GEN_DIR}/qhelpsearchengine_wrapper.cpp
+${QtHelp_GEN_DIR}/qhelpsearchenginecore_wrapper.cpp
 ${QtHelp_GEN_DIR}/qhelpsearchquery_wrapper.cpp
 ${QtHelp_GEN_DIR}/qhelpsearchquerywidget_wrapper.cpp
 ${QtHelp_GEN_DIR}/qhelpsearchresult_wrapper.cpp
index 5fa30e03473b33e53e036acc1697fb3c7b7c8cb9..204fd56339a618da4a848d2aaf74b32928b6dacd 100644 (file)
@@ -9,24 +9,29 @@
     <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
 
     <value-type name="QCompressedHelpInfo"/>
-    <value-type name="QHelpContentItem">
+    <object-type name="QHelpContentItem"> <!-- Non-copyable, non-movable in 6.8 -->
       <modify-function signature="parent()const">
         <modify-argument index="return">
           <define-ownership owner="default"/>
         </modify-argument>
       </modify-function>
-    </value-type>
+    </object-type>
     <object-type name="QHelpContentModel" polymorphic-id-expression="qobject_cast&lt;QHelpContentModel*&gt;(%B)"/>
     <object-type name="QHelpContentWidget"/>
     <value-type name="QHelpGlobal"/>
     <object-type name="QHelpEngine"/>
-    <object-type name="QHelpEngineCore"/>
+    <object-type name="QHelpEngineCore">
+       <!-- Uses QFuture -->
+       <modify-function signature="requestIndexForCurrentFilter()const" remove="all"/>
+       <modify-function signature="requestIndex(QString)const" remove="all"/>
+    </object-type>
     <value-type name="QHelpFilterData"/>
     <object-type name="QHelpFilterEngine"/>
     <object-type name="QHelpFilterSettingsWidget"/>
     <object-type name="QHelpIndexModel"/>
     <object-type name="QHelpIndexWidget"/>
     <value-type name="QHelpLink"/>
+    <object-type name="QHelpSearchEngineCore"/>
     <object-type name="QHelpSearchEngine"/>
     <value-type name="QHelpSearchQuery">
         <enum-type name="FieldName"/>
index c931f064f62681149b4cd4341aae63cfef058502..b2a4fda85942d7b7ed76c05556cdf7e7972fbc03 100644 (file)
@@ -12,6 +12,7 @@ set(QtHttpServer_SRC
     ${QtHttpServer_GEN_DIR}/qhttpserverresponse_wrapper.cpp
     ${QtHttpServer_GEN_DIR}/qhttpserverrouter_wrapper.cpp
     ${QtHttpServer_GEN_DIR}/qhttpserverrouterrule_wrapper.cpp
+    ${QtHttpServer_GEN_DIR}/qhttpserverwebsocketupgraderesponse_wrapper.cpp
 # module is always needed
     ${QtHttpServer_GEN_DIR}/qthttpserver_module_wrapper.cpp)
 
index 026a8758a11f994a1109c0e73ccf1b58aef1bf2d..66b2ff9eafae96f29f408c3dbb62476cb9c28cc2 100644 (file)
@@ -21,9 +21,9 @@
         <inject-code class="target" position="beginning" file="../glue/qhttpserver.cpp"
                      snippet="qhttpserver-route"/>
       </add-function>
-      <add-function signature="afterRequest(PyCallable @callback@)">
+      <add-function signature="addAfterRequestHandler(QObject*@context@,PyCallable @callback@)">
         <inject-code class="target" position="beginning" file="../glue/qhttpserver.cpp"
-                     snippet="qhttpserver-afterrequest"/>
+                     snippet="qhttpserver-addafterrequesthandler"/>
       </add-function>
   </object-type>
   <object-type name="QHttpServerResponder">
@@ -35,4 +35,7 @@
   <object-type name="QHttpServerResponse"/>
   <object-type name="QHttpServerRouter"/>
   <object-type name="QHttpServerRouterRule"/>
+  <object-type name="QHttpServerWebSocketUpgradeResponse" since="6.8">
+      <enum-type name="ResponseType"/>
+  </object-type>
 </typesystem>
index db5777e04abc8c4f90d8c88f50d950d08a17392c..659aa095ecadc0d029ec0a18950debaca8acb1b7 100644 (file)
@@ -13,6 +13,7 @@ ${QtLocation_GEN_DIR}/qgeoroute_wrapper.cpp
 ${QtLocation_GEN_DIR}/qgeoroutereply_wrapper.cpp
 ${QtLocation_GEN_DIR}/qgeoroutesegment_wrapper.cpp
 ${QtLocation_GEN_DIR}/qgeoserviceprovider_wrapper.cpp
+${QtLocation_GEN_DIR}/qlocation_wrapper.cpp
 ${QtLocation_GEN_DIR}/qplace_wrapper.cpp
 ${QtLocation_GEN_DIR}/qplacecontentreply_wrapper.cpp
 ${QtLocation_GEN_DIR}/qplacedetailsreply_wrapper.cpp
index e35b7bd11adb91d50cef9f927433bf2da799ebbd..f3b5f0b2b05216f57b3fa415f838100a3acc2091 100644 (file)
@@ -7,6 +7,9 @@
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
   <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
   <load-typesystem name="QtPositioning/typesystem_positioning.xml" generate="no"/>
+  <namespace-type name="QLocation">
+      <enum-type name="Visibility" flags="VisibilityScope"/>
+  </namespace-type>
   <object-type name="QGeoCodeReply">
       <enum-type name="Error"/>
   </object-type>
index e40c5a2edd805ea02ef4aa957642effa6531a9d6..8e6d3e3ce89624d8066cee253d2e286c500d0a9b 100644 (file)
@@ -4,7 +4,11 @@
 project(QtMultimedia)
 
 set(QtMultimedia_SRC
+${QtMultimedia_GEN_DIR}/qabstractvideobuffer_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qabstractvideobuffer_mapdata_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qaudiobuffer_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qaudiobufferinput_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qaudiobufferoutput_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qaudiodecoder_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qaudiodevice_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qaudioformat_wrapper.cpp
@@ -30,9 +34,10 @@ ${QtMultimedia_GEN_DIR}/qmediatimerange_interval_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qscreencapture_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qsoundeffect_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qtvideo_wrapper.cpp
-${QtMultimedia_GEN_DIR}/qvideoframe_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qvideoframe_paintoptions_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qvideoframe_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qvideoframeformat_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qvideoframeinput_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qvideosink_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qwavedecoder_wrapper.cpp
 ${QtMultimedia_GEN_DIR}/qwindowcapture_wrapper.cpp
index 2791f695a1655841f60b81e8d197c0040d76c4d1..8a8eb5e29700933d17b271983871ca65d49fce71 100644 (file)
@@ -21,7 +21,9 @@
     <namespace-type name="QtVideo" since="6.7">
         <enum-type name="Rotation"/>
     </namespace-type>
-
+    <object-type name="QAbstractVideoBuffer" since="6.8">
+        <value-type name="MapData"/>
+    </object-type>
     <value-type name="QAudioBuffer">
         <add-function signature="data()" return-type="PyBuffer">
             <inject-code file="../glue/qtmultimedia.cpp" snippet="qaudiobuffer-data"/>
@@ -30,6 +32,8 @@
             <inject-code file="../glue/qtmultimedia.cpp" snippet="qaudiobuffer-const-data"/>
         </add-function>
     </value-type>
+    <object-type name="QAudioBufferInput" since="6.8"/>
+    <object-type name="QAudioBufferOutput" since="6.8"/>
     <object-type name="QAudioDecoder">
         <enum-type name="Error"/>
     </object-type>
         <enum-type name="PlaybackState" since="6.1"/>
         <enum-type name="Error"/>
         <enum-type name="Loops" python-type="IntEnum" since="6.2.3"/>
+        <modify-function signature="setSource(QUrl)" allow-thread="true"/>
     </object-type>
     <!-- see qtmultimedia/5773f7214c7430a98dea3974c0597cb3ee0ea7f5 might reappear in 6.3
     <object-type name="QMediaPlaylist"/>
         <enum-type name="PixelFormat"/>
         <enum-type name="YCbCrColorSpace"/>
     </value-type>
+    <object-type name="QVideoFrameInput" since="6.8"/>
 
     <object-type name="QWaveDecoder">
         <!-- No implementation -->
index 05b4e2456bd8cd94332d717f7a5fae7809415522..c20f5c644f5a51dc8916317819cd4980aeaf3428 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2021 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtMultimediaWidgets"
+<typesystem package="PySide6.QtMultimediaWidgets" doc-package="PySide6.QtMultimedia"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
   <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
   <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
index 529e2e86b5d35666143c12289525306b7add47f2..ca2f02c140c0bd8e88f4ebb63a3f5e78e4c9d1ab 100644 (file)
@@ -15,6 +15,9 @@ ${QtNetwork_GEN_DIR}/qdnslookup_wrapper.cpp
 ${QtNetwork_GEN_DIR}/qdnsmailexchangerecord_wrapper.cpp
 ${QtNetwork_GEN_DIR}/qdnsservicerecord_wrapper.cpp
 ${QtNetwork_GEN_DIR}/qdnstextrecord_wrapper.cpp
+${QtNetwork_GEN_DIR}/qdnstlsassociationrecord_wrapper.cpp
+${QtNetwork_GEN_DIR}/qformdatabuilder_wrapper.cpp
+${QtNetwork_GEN_DIR}/qformdatapartbuilder_wrapper.cpp
 ${QtNetwork_GEN_DIR}/qhostaddress_wrapper.cpp
 ${QtNetwork_GEN_DIR}/qhostinfo_wrapper.cpp
 ${QtNetwork_GEN_DIR}/qhstspolicy_wrapper.cpp
index 4dc7c9b0a9ff5f16d49a918c8db4011a5394621b..9f727547102201a70b66b14ba24e17ca90934115 100644 (file)
     <value-type name="QDnsHostAddressRecord"/>
     <object-type name="QDnsLookup">
         <enum-type name="Error"/>
+        <enum-type name="Protocol" since="6.8"/>
         <enum-type name="Type"/>
     </object-type>
     <value-type name="QDnsMailExchangeRecord"/>
     <value-type name="QDnsServiceRecord"/>
     <value-type name="QDnsTextRecord"/>
-
+    <value-type name="QDnsTlsAssociationRecord" since="6.8">
+        <enum-type name="CertificateUsage"/>
+        <enum-type name="Selector"/>
+        <enum-type name="MatchingType"/>
+    </value-type>
     <object-type name="QDtls">
         <enum-type name="HandshakeState"/>
         <configuration condition="QT_CONFIG(dtls)"/>
         </value-type>
     </object-type>
 
+    <object-type name="QFormDataPartBuilder" since="6.8"/>
+    <object-type name="QFormDataBuilder" since="6.8">
+        <enum-type name="Option" flags="Options"/>
+    </object-type>
+
     <value-type name="QHstsPolicy">
         <enum-type name="PolicyFlag" flags="PolicyFlags"/>
     </value-type>
             <define-ownership class="target" owner="default"/>
           </modify-argument>
         </modify-function>
-        <modify-function signature="get(const QNetworkRequest&amp;)" allow-thread="yes"/>
-        <modify-function signature="post(const QNetworkRequest &amp;,QIODevice*)" allow-thread="yes"/>
-        <modify-function signature="post(const QNetworkRequest &amp;,const QByteArray &amp;)" allow-thread="yes"/>
-        <modify-function signature="put(const QNetworkRequest &amp;,QIODevice*)" allow-thread="yes"/>
-        <modify-function signature="put(const QNetworkRequest &amp;,const QByteArray &amp;)" allow-thread="yes"/>
-        <modify-function signature="sendCustomRequest(const QNetworkRequest &amp;,const QByteArray &amp;,QIODevice*)" allow-thread="yes" since="4.7"/>
+        <modify-function signature="head(QNetworkRequest)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+            <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="get(QNetworkRequest)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+             <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="get(QNetworkRequest,QIODevice*)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+             <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="get(QNetworkRequest,QByteArray)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+             <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="post(QNetworkRequest,QIODevice*)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+            <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="post(QNetworkRequest,QByteArray)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+            <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="put(QNetworkRequest,QIODevice*)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+            <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="put(QNetworkRequest,QByteArray)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+            <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="sendCustomRequest(QNetworkRequest,QByteArray,QIODevice*)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+            <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
+        <modify-function signature="sendCustomRequest(QNetworkRequest,QByteArray,QByteArray)" allow-thread="yes">
+          <modify-argument index="0"> <!-- Suppress return value heuristics -->
+            <define-ownership class="target" owner="default"/>
+          </modify-argument>
+        </modify-function>
         <modify-function signature="setCache(QAbstractNetworkCache*)">
             <modify-argument index="1">
                 <define-ownership class="target" owner="c++"/>
         <inject-code class="native" position="beginning" file="../glue/qtnetwork.cpp"
                      snippet="qhostinfo-lookuphost-functor"/>
         <enum-type name="HostInfoError"/>
-        <add-function signature="lookupHost(const QString &amp;,PyCallable)">
+        <add-function signature="lookupHost(const QString &amp;@name@,PyCallable@callable@)">
             <inject-code class="target" position="beginning"
                          file="../glue/qtnetwork.cpp"
                          snippet="qhostinfo-lookuphost-callable"/>
     </value-type>
 
     <value-type name="QIPv6Address">
-        <add-function signature="__len__">
+        <add-function signature="__len__" return-type="int">
             <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp" snippet="qipv6address-len"/>
         </add-function>
         <add-function signature="__getitem__">
 
     <!-- The above entries may be present in the system or not. Keep this section organized. -->
 </typesystem>
-
index 4d9498e4d0281837c3520bdf307608a92366db6f..f713ff10913d45c77dce4cb84c6fb3fd2ee96ff7 100644 (file)
@@ -12,6 +12,7 @@ ${QtNetworkAuth_GEN_DIR}/qoauth1signature_wrapper.cpp
 ${QtNetworkAuth_GEN_DIR}/qoauth2authorizationcodeflow_wrapper.cpp
 ${QtNetworkAuth_GEN_DIR}/qoauthhttpserverreplyhandler_wrapper.cpp
 ${QtNetworkAuth_GEN_DIR}/qoauthoobreplyhandler_wrapper.cpp
+${QtNetworkAuth_GEN_DIR}/qoauthurischemereplyhandler_wrapper.cpp
 # module is always needed
 ${QtNetworkAuth_GEN_DIR}/qtnetworkauth_module_wrapper.cpp
 )
index bedfc5a2875f12cf3a14ec8747a758d4b1c05890..888f3d43d6ac937bbe0b4998e6e036c8b2070740 100644 (file)
     <value-type name="QOAuth1Signature">
         <enum-type name="HttpRequestMethod"/>
     </value-type>
-    <object-type name="QOAuth2AuthorizationCodeFlow"/>
+    <object-type name="QOAuth2AuthorizationCodeFlow">
+        <enum-type name="PkceMethod" since="6.8"/>
+    </object-type>
     <object-type name="QOAuthHttpServerReplyHandler"/>
     <object-type name="QOAuthOobReplyHandler"/>
+    <object-type name="QOAuthUriSchemeReplyHandler" since="6.8"/>
 
     <suppress-warning text="^.*Typedef used on signal QAbstractOAuth.*$"/>
 </typesystem>
index 1e308a3f2cc71efb8101e115d7525556b09df3cf..0d7fdfaa13289e8ee1726c7952ed49c8ace48645 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2020 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtOpenGLWidgets"
+<typesystem package="PySide6.QtOpenGLWidgets" doc-package="PySide6.QtOpenGL"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
   <load-typesystem name="QtCore/typesystem_core.xml" generate="no" />
   <load-typesystem name="QtGui/typesystem_gui.xml" generate="no" />
index 5df72075c8e11c10e4beed00d624dada2cb4d7f9..8228bf723a45bef60cd37fc1eaa09979f4f77729 100644 (file)
@@ -4,7 +4,8 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
 <typesystem package="PySide6.QtPdfWidgets"
-            namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
+            namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE"
+            doc-package="PySide6.QtPdf">
     <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
     <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
     <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
index 126902b73f23001eb5df3c3e7f1e542e75ca2cb2..aa64abed296e49a5a1a8b3cbc1bdc679933d1b61 100644 (file)
@@ -55,7 +55,7 @@
     <!-- fixme: Check if this is still required in Qt 6:
         bool QPagedPaintDevice::setPageSize(QPageSize)
         void QPagedPaintDevice::setPageSize(QPagedPaintDevice::PageSize) -->
-    <add-function signature="setPageSize(const QPageSize&amp;)" return-type="bool">
+    <add-function signature="setPageSize(const QPageSize&amp;@size@)" return-type="bool">
         <inject-code file="../glue/qtprintsupport.cpp" snippet="setpagesize" />
     </add-function>
   </object-type>
@@ -68,4 +68,3 @@
   </object-type>
 
 </typesystem>
-
index ca3dfebeda5f06dc8f88121f2069158e08a17504..2cab76b471c471fd65caa43c33f5a68a4c971ca0 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <pep384ext.h>
 #include <signature.h>
+#include <sbktypefactory.h>
 
 #include <QtCore/QDebug>
 
index 3392ef379ef19f3b5d70534c7490aa6b64c261f1..8477ac399f133338550493f3195fe383abfb6641 100644 (file)
@@ -9,6 +9,11 @@
     <load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
 
     <rejection class="QQmlPrivate"/>
+    <rejection class="*" enum-name="QmlIsAnonymous"/>
+    <rejection class="*" enum-name="QmlIsUncreatable"/>
+    <rejection class="*" enum-name="QmlIsSingleton"/>
+    <rejection class="*" enum-name="QmlIsInterface"/>
+    <rejection class="*" enum-name="QmlIsSequence"/>
 
     <inject-code class="target" position="declaration">
     // Volatile Bool Ptr type definition for QQmlIncubationController::incubateWhile(std::atomic&lt;bool&gt; *, int)
 
     <add-function signature="qmlRegisterType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@)" return-type="int">
         <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistertype"/>
-        <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+        <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                               snippet="qmlregistertype"/>
         <modify-argument index="2" pyi-type="str"/>
     </add-function>
 
     <add-function signature="qmlRegisterSingletonType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
       <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_callback"/>
-      <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+      <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                             snippet="qmlregistersingletontype_qobject_callback"/>
       <modify-argument index="2" pyi-type="str"/>
     </add-function>
 
     <add-function signature="qmlRegisterSingletonType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@)" return-type="int">
       <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_nocallback"/>
-      <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+      <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                             snippet="qmlregistersingletontype_qobject_nocallback"/>
       <modify-argument index="2" pyi-type="str"/>
     </add-function>
 
     <add-function signature="qmlRegisterSingletonType(const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
       <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qjsvalue"/>
-      <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+      <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                             snippet="qmlregistersingletontype_qjsvalue"/>
       <modify-argument index="1" pyi-type="str"/>
     </add-function>
 
     <add-function signature="qmlRegisterSingletonInstance(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
       <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletoninstance"/>
-      <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+      <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                             snippet="qmlregistersingletoninstance"/>
       <modify-argument index="2" pyi-type="str"/>
     </add-function>
 
     <add-function signature="qmlRegisterUncreatableType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,const char*@message@)" return-type="int">
         <inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregisteruncreatabletype"/>
-        <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+        <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                               snippet="qmlregisteruncreatabletype"/>
         <modify-argument index="2" pyi-type="str"/>
     </add-function>
     <object-type name="QJSEngine">
         <enum-type name="Extension" flags="Extensions"/>
         <enum-type name="ObjectOwnership"/>
-        <add-function signature="toScriptValue(const QVariant&amp;)" return-type="QJSValue">
+        <add-function signature="toScriptValue(const QVariant&amp;@value@)" return-type="QJSValue">
             <inject-code class="target" position="end" file="../glue/qtqml.cpp" snippet="qjsengine-toscriptvalue"/>
         </add-function>
     </object-type>
         <value-type name="PropertyPair"/>
     </object-type>
     <value-type name="QQmlError">
-        <add-function signature="__repr__" return-type="PyObject*">
+        <add-function signature="__repr__" return-type="str">
             <inject-code class="target" position="beginning" file="../glue/qtqml.cpp" snippet="qmlerrror-repr"/>
         </add-function>
     </value-type>
             </modify-argument>
             <inject-code class="target" file="../glue/qtqml.cpp"
                          snippet="qqmlengine-singletoninstance-qmltypeid"/>
-            <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+            <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                                   snippet="qqmlengine-singletoninstance-qmltypeid"/>
         </add-function>
         <add-function signature="singletonInstance(QString@uri@,QString@typeName@)"
             </modify-argument>
             <inject-code class="target" file="../glue/qtqml.cpp"
                          snippet="qqmlengine-singletoninstance-typename"/>
-            <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+            <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
                                   snippet="qqmlengine-singletoninstance-typename"/>
         </add-function>
     </object-type>
index 7752302428eee0c68180f0552c39f2b61851e6c3..a2bef339fd1d76f6cac7f9b9a1e8fe7a8111685e 100644 (file)
@@ -104,9 +104,10 @@ check_qt_opengl("Quick" QtQuick_include_dirs QtQuick_deps
 if (Qt${QT_MAJOR_VERSION}OpenGL_FOUND)
     list(APPEND QtQuick_SRC
          ${QtQuick_GEN_DIR}/qquickframebufferobject_wrapper.cpp
-         ${QtQuick_GEN_DIR}/qquickframebufferobject_renderer_wrapper.cpp)
+         ${QtQuick_GEN_DIR}/qquickframebufferobject_renderer_wrapper.cpp
+         ${QtQuick_GEN_DIR}/qquickopenglutils_wrapper.cpp)
 else()
-    list(APPEND QtQuick_DROPPED_ENTRIES QQuickFramebufferObject)
+    list(APPEND QtQuick_DROPPED_ENTRIES QQuickFramebufferObject QQuickOpenGLUtils)
 endif()
 
 create_pyside_module(NAME QtQuick
index 0a24123f4d1b0aa1dfa381f4dd04a1b42e38e160..853c1589f7d96d5e14cf2f4932e0a6cdafe21a0d 100644 (file)
@@ -39,6 +39,7 @@
             </modify-argument>
         </modify-function>
     </object-type>
+    <namespace-type name="QQuickOpenGLUtils"/>
 
     <value-type name="QQuickGraphicsConfiguration"/>
     <value-type name="QQuickGraphicsDevice">
@@ -53,7 +54,7 @@
     <object-type name="QQuickImageResponse"/>
 
     <object-type name="QQuickTransform"/>
-    <object-type name="QQuickItem" delete-in-main-thread="true">
+    <object-type name="QQuickItem" delete-in-main-thread="true" polymorphic-base="true">
         <value-type name="UpdatePaintNodeData"/>
         <enum-type name="Flag" flags="Flags"/>
         <enum-type name="ItemChange"/>
@@ -96,6 +97,7 @@
         <!-- PYSIDE-1726, after qtbase/696d94b132b2f352b5e6b889ad91c2437417fae8,
              the function is defined with dummy types when Vulkan is not present. -->
         <modify-function signature="^fromVulkanImage\(.*$" remove="all"/>
+        <enum-type name="Flag" flags="Flags" since="6.8"/>
     </value-type>
 
     <object-type name="QQuickTextDocument">
         </value-type>
         <value-type name="GraphicsPipelineState">
             <enum-type name="BlendFactor"/>
+            <enum-type name="BlendOp" since="6.8"/>
             <enum-type name="ColorMaskComponent" flags="ColorMask"/>
             <enum-type name="CullMode"/>
             <enum-type name="PolygonMode"/>
index fe8f4e700ee1f4c6863b841b6d25dda1068b7c49..b981de4bc000604286d41e27230d3c46cf9b58b9 100644 (file)
     </object-type>
     <object-type name="QQuick3DInstancing">
         <value-type name="InstanceTableEntry"/>
+        <add-function signature="getInstanceBufferOverride()"
+                      return-type="std::pair&lt;QByteArray,int&gt;" python-override="true"/>
+        <modify-function signature="getInstanceBuffer(int*)">
+          <inject-code class="shell" position="override" file="../glue/qtquick3d.cpp"
+                       snippet="qquick3dinstancing-getinstancebuffer-virtual-redirect"/>
+          <modify-argument index="return" pyi-type="Tuple[bool, str]"/>
+          <modify-argument index="1"><remove-default-expression/><remove-argument/></modify-argument>
+          <inject-code class="target" position="beginning" file="../glue/qtquick3d.cpp"
+                       snippet="qquick3dinstancing-getinstancebuffer-return"/>
+        </modify-function>
     </object-type>
     <object-type name="QQuick3DTextureData">
         <enum-type name="Format"/>
index 72fc05226910367cf2f037f043f4aae2651a884e..3893722268748e93fe844471a8d20a96f19a5c01 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2020 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtQuickControls2"
+<typesystem package="PySide6.QtQuickControls2" doc-package="PySide6.QtQuickControls"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="QtQuick/typesystem_quick.xml" generate="no"/>
 
index 4f4484cb0aa7c3ec4c04849c72127106b538e0f4..410ae2d21a79717c1758b82db6b0725ce538e20e 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtQuickWidgets"
+<typesystem package="PySide6.QtQuickWidgets" doc-package="PySide6.QtQuick"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
     <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
index fdd2b148353c5d78de4f2b52c92b5af619690558..6cf562c52924637404a56c382adfedd2f95cbbac 100644 (file)
@@ -19,8 +19,8 @@
 
     <object-type name="QCanBus">
         <!-- Remove errorMessage argument, return tuple instead.  -->
-        <modify-function signature="availableDevices(QString,QString*)const">
-            <modify-argument index="2">
+        <modify-function signature="availableDevices(QString*)const">
+            <modify-argument index="1">
                 <remove-default-expression/>
                 <remove-argument/>
             </modify-argument>
             The function returns a tuple of (device_list, error_string).
             </inject-documentation>
         </modify-function>
+        <!-- Remove errorMessage argument, return tuple instead.  -->
+        <modify-function signature="availableDevices(QString,QString*)const">
+            <modify-argument index="2">
+                <remove-default-expression/>
+                <remove-argument/>
+            </modify-argument>
+            <modify-argument index="return">
+                <replace-type modified-type="tuple"/>
+            </modify-argument>
+            <inject-code class="target" position="beginning"
+                         file="../glue/qtserialbus.cpp" snippet="qcanbus-available-devices-plugin"/>
+            <inject-documentation format="target" mode="append" emphasis="language-note">
+            The function returns a tuple of (device_list, error_string).
+            </inject-documentation>
+        </modify-function>
+
         <modify-function signature="createDevice(QString,QString,QString*)const">
             <modify-argument index="3">
                 <remove-default-expression/>
@@ -44,7 +60,7 @@
             </modify-argument>
             <inject-code class="target" position="beginning"
                          file="../glue/qtserialbus.cpp" snippet="qcanbus-createdevice"/>
-            <inject-documentation format="target" mode="append">
+            <inject-documentation format="target" mode="append" emphasis="language-note">
             The function returns a tuple of (device, error_string).
             </inject-documentation>
         </modify-function>
         <value-type name="MultiplexValueRange"/>
     </value-type>
     <value-type name="QCanUniqueIdDescription"/>
-    <object-type name="QModbusClient"/>
+    <object-type name="QModbusClient">
+        <modify-function signature="sendReadRequest(QModbusDataUnit,int)">
+            <modify-argument index="0"> <!-- Suppress return value heuristics -->
+                <define-ownership class="target" owner="default"/>
+            </modify-argument>
+        </modify-function>
+        <modify-function signature="sendWriteRequest(QModbusDataUnit,int)">
+            <modify-argument index="0"> <!-- Suppress return value heuristics -->
+                <define-ownership class="target" owner="default"/>
+            </modify-argument>
+        </modify-function>
+        <modify-function signature="sendReadWriteRequest(QModbusDataUnit,QModbusDataUnit,int)">
+            <modify-argument index="0"> <!-- Suppress return value heuristics -->
+                <define-ownership class="target" owner="default"/>
+            </modify-argument>
+        </modify-function>
+        <modify-function signature="sendRawRequest(QModbusRequest,int)">
+            <modify-argument index="0"> <!-- Suppress return value heuristics -->
+                <define-ownership class="target" owner="default"/>
+            </modify-argument>
+        </modify-function>
+    </object-type>
     <value-type name="QModbusDataUnit">
         <enum-type name="RegisterType"/>
     </value-type>
index b9c2557afb3899d04c66079a9eac6699c1d82b82..98c18f07bf28ef063031943f34e132974f22095e 100644 (file)
@@ -11,7 +11,7 @@
     <load-typesystem name="QtMultimedia/typesystem_multimedia.xml" generate="no"/>
 
     <object-type name="QAmbientSound">
-        <enum-type name="Loops"/>
+        <enum-type name="Loops" python-type="IntEnum"/>
     </object-type>
 
     <object-type name="QAudioEngine">
@@ -26,7 +26,7 @@
     </object-type>
 
     <object-type name="QSpatialSound">
-        <enum-type name="Loops"/>
+        <enum-type name="Loops" python-type="IntEnum"/>
         <enum-type name="DistanceModel"/>
     </object-type>
 </typesystem>
index 0e29f240f65d55a8f01cd91f4325f0eeb1c0fcac..369c78bd0d97cc130156920bfe3c7dc8ff9f2959 100644 (file)
@@ -108,7 +108,7 @@ s1.addTransition(button.clicked, s1h)&lt;/code>
   </object-type>
 
   <object-type name="QSignalTransition">
-    <add-function signature="QSignalTransition(PyObject*,QState*)" return-type="QSignalTransition*">
+    <add-function signature="QSignalTransition(PyObject*@signal@,QState*@state@)">
         <modify-argument index="2">
             <replace-default-expression with="0"/>
         </modify-argument>
@@ -146,7 +146,7 @@ s1.addTransition(button.clicked, s1h)&lt;/code>
     <!-- FIXME: the proper signature for this added function would be something like
          addTransition(PySide2.QtCore.Signal, QAbstractState*)
          but that depends on bug #362. -->
-    <add-function signature="addTransition(PyObject*,QAbstractState*)" return-type="QSignalTransition*">
+    <add-function signature="addTransition(PyObject*@signal@,QAbstractState*)" return-type="QSignalTransition*">
       <modify-argument index="return">
         <parent index="this" action="add"/>
       </modify-argument>
@@ -179,6 +179,17 @@ s1.addTransition(button.clicked, s1h)&lt;/code>
       </modify-argument>
     </modify-function>
 
+    <modify-function signature="postEvent(QEvent*,QStateMachine::EventPriority)">
+      <modify-argument index="1">
+        <define-ownership owner="c++"/>
+      </modify-argument>
+    </modify-function>
+    <modify-function signature="postDelayedEvent(QEvent*,int)">
+      <modify-argument index="1">
+        <define-ownership owner="c++"/>
+      </modify-argument>
+    </modify-function>
+
     <add-function signature="configuration()" return-type="QSet&lt;QAbstractState*&gt;">
         <inject-code class="target" position="beginning" file="../glue/qtstatemachine.cpp"
                      snippet="qstatemachine-configuration"/>
index 36bda1ea3ba601f21c08cf2911c675277b9f9f79..b9271427d875423fda2ede4aaa7c7814f2c7d469 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2020 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtSvgWidgets"
+<typesystem package="PySide6.QtSvgWidgets" doc-package="PySide6.QtSvg"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
   <load-typesystem name="QtSvg/typesystem_svg.xml" generate="no"/>
   <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
index 2fc4f23cd0c5862a21f666b6f70ab3f3c4993044..173392f3b739e3c748bf7fac47c4be02daa90486 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtTest"
+<typesystem package="PySide6.QtTest" doc-package="PySide6.QtTestlib"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
   <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
   <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
index 0cdaf2f911446163dcac294d62b97f7369b575e4..9cc3d87e9976c061c91b3e74750d91af4d825457 100644 (file)
@@ -3,17 +3,26 @@
 
 project(QtWebEngineCore)
 
+set(QtWebEngineCore_static_sources
+    "${QtWebEngineCore_SOURCE_DIR}/glue/webenginepage_functors.cpp"
+    "${QtWebEngineCore_SOURCE_DIR}/glue/webenginepage_functors.h"
+    "${QtWebEngineCore_SOURCE_DIR}/glue/webengineframe.cpp"
+    "${QtWebEngineCore_SOURCE_DIR}/glue/webengineframe.h"
+)
+
 set(QtWebEngineCore_SRC
 ${QtWebEngineCore_GEN_DIR}/qwebenginecertificateerror_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebengineclientcertificateselection_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebengineclientcertificatestore_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineclienthints_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginecontextmenurequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginecookiestore_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginecookiestore_filterrequest_wrapper.cpp
-# FIXME ${QtWebEngineCore_GEN_DIR}/qwebenginedesktopmediarequest_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginedesktopmediarequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginedownloadrequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginefilesystemaccessrequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginefindtextresult_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineframe_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginefullscreenrequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebengineglobalsettings_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebengineglobalsettings_dnsmode_wrapper.cpp
@@ -23,10 +32,10 @@ ${QtWebEngineCore_GEN_DIR}/qwebenginehistorymodel_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginehttprequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginenavigationrequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebengineloadinginfo_wrapper.cpp
-# FIXME ${QtWebEngineCore_GEN_DIR}/qwebenginemediasourcemodel_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginenewwindowrequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginenotification_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginepage_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginepermission_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebengineprofile_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebenginequotarequest_wrapper.cpp
 ${QtWebEngineCore_GEN_DIR}/qwebengineregisterprotocolhandlerrequest_wrapper.cpp
@@ -68,8 +77,8 @@ set(QtWebEngineCore_deps QtCore QtGui QtNetwork QtPrintSupport QtWebChannel)
 
 create_pyside_module(NAME QtWebEngineCore
                      INCLUDE_DIRS QtWebEngineCore_include_dirs
+                     STATIC_SOURCES QtWebEngineCore_static_sources
                      LIBRARIES QtWebEngineCore_libraries
                      DEPS QtWebEngineCore_deps
                      TYPESYSTEM_PATH QtWebEngineCore_SOURCE_DIR
                      SOURCES QtWebEngineCore_SRC)
-
diff --git a/sources/pyside6/PySide6/QtWebEngineCore/glue/webengineframe.cpp b/sources/pyside6/PySide6/QtWebEngineCore/glue/webengineframe.cpp
new file mode 100644 (file)
index 0000000..fb209d5
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "webengineframe.h"
+
+#include <QtWebEngineCore/qwebengineframe.h>
+
+QT_BEGIN_NAMESPACE
+
+// Enable using QWebEngineFrame as a value-type by adding a way of
+// default-constructing by creating a replica with the same data members.
+// (see attribute "default-constructor").
+QWebEngineFrame defaultConstructedWebEngineFrame()
+{
+    class FriendlyWebEngineFrame // Keep in sync with QWebEngineFrame
+    {
+    public:
+        QWeakPointer<QObject> m_w;
+        quint64 m_id = 0;
+    };
+
+    FriendlyWebEngineFrame frame;
+    return std::move(*reinterpret_cast<QWebEngineFrame*>(&frame));
+}
+
+QT_END_NAMESPACE
diff --git a/sources/pyside6/PySide6/QtWebEngineCore/glue/webengineframe.h b/sources/pyside6/PySide6/QtWebEngineCore/glue/webengineframe.h
new file mode 100644 (file)
index 0000000..a792c7b
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef WEBENGINEFRAME_H
+#define WEBENGINEFRAME_H
+
+#include <QtCore/QtClassHelperMacros>
+
+QT_FORWARD_DECLARE_CLASS(QWebEngineFrame)
+
+QT_BEGIN_NAMESPACE
+
+QWebEngineFrame defaultConstructedWebEngineFrame();
+
+QT_END_NAMESPACE
+
+#endif // WEBENGINEFRAME_H
diff --git a/sources/pyside6/PySide6/QtWebEngineCore/glue/webenginepage_functors.cpp b/sources/pyside6/PySide6/QtWebEngineCore/glue/webenginepage_functors.cpp
new file mode 100644 (file)
index 0000000..3591591
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "webenginepage_functors.h"
+
+#include "autodecref.h"
+#include "gilstate.h"
+#include "sbkconverter.h"
+
+#include "pysideutils.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+void RunJavascriptFunctor::operator()(const QVariant &result)
+{
+    Shiboken::GilState state;
+    Shiboken::AutoDecRef arglist(PyTuple_New(1));
+    switch (result.typeId()) {
+    case QMetaType::Bool: {
+        PyObject *pyValue = result.toBool() ? Py_True : Py_False;
+        Py_INCREF(pyValue);
+        PyTuple_SetItem(arglist, 0, pyValue);
+    }
+    break;
+    case QMetaType::Int:
+    case QMetaType::UInt:
+    case QMetaType::LongLong:
+    case QMetaType::ULongLong:
+    case QMetaType::Double:
+        PyTuple_SetItem(arglist, 0, PyFloat_FromDouble(result.toDouble()));
+    break;
+    default: {
+        const QString value = result.toString();
+        PyTuple_SetItem(arglist, 0, PySide::qStringToPyUnicode(value));
+    }
+    break;
+    }
+    Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
+    release(); // single shot
+}
+
+void PrintToPdfFunctor::operator()(const QByteArray &pdf)
+{
+    Shiboken::GilState state;
+    Shiboken::AutoDecRef arglist(PyTuple_New(1));
+
+    Shiboken::Conversions::SpecificConverter converter("QByteArray");
+    PyObject *pyPdf = converter.toPython(&pdf);
+    PyTuple_SetItem(arglist, 0, pyPdf);
+    Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
+    release(); // single shot
+}
+
+QT_END_NAMESPACE
diff --git a/sources/pyside6/PySide6/QtWebEngineCore/glue/webenginepage_functors.h b/sources/pyside6/PySide6/QtWebEngineCore/glue/webenginepage_functors.h
new file mode 100644 (file)
index 0000000..535c279
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef WEBENGINEPAGE_FUNCTORS_H
+#define WEBENGINEPAGE_FUNCTORS_H
+
+#include "pyobjectholder.h"
+
+#include <QtCore/QtClassHelperMacros>
+
+QT_FORWARD_DECLARE_CLASS(QByteArray)
+QT_FORWARD_DECLARE_CLASS(QVariant)
+
+QT_BEGIN_NAMESPACE
+
+struct RunJavascriptFunctor : public Shiboken::PyObjectHolder
+{
+    using Shiboken::PyObjectHolder::PyObjectHolder;
+
+    void operator()(const QVariant &result);
+};
+
+struct PrintToPdfFunctor : public Shiboken::PyObjectHolder
+{
+    using Shiboken::PyObjectHolder::PyObjectHolder;
+
+    void operator()(const QByteArray &pdf);
+};
+
+QT_END_NAMESPACE
+
+#endif // WEBENGINEPAGE_FUNCTORS_H
index da9259ccc14488f5d1f0cde3490eb901235ca7e6..36c15c4e96d06b2ee613a906c58054046328e210 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2018 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtWebEngineCore"
+<typesystem package="PySide6.QtWebEngineCore" doc-package="PySide6.QtWebEngine"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
   <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
   <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
   <load-typesystem name="QtPrintSupport/typesystem_printsupport.xml" generate="no"/>
   <load-typesystem name="QtWebChannel/typesystem_webchannel.xml" generate="no"/>
 
+   <extra-includes>
+       <include file-name="glue/webengineframe.h" location="global"/>
+   </extra-includes>
+
    <rejection class="extensions"/>
 
   <function signature="qWebEngineChromiumVersion()"/>
@@ -34,6 +38,8 @@
     <enum-type name="Type"/>
   </value-type>
 
+  <object-type name="QWebEngineClientHints" since="6.8"/>
+
   <object-type name="QWebEngineContextMenuRequest">
     <enum-type name="EditFlag" flags="EditFlags"/>
     <enum-type name="MediaFlag" flags="MediaFlags"/>
     <modify-function signature="swap(QWebEngineFileSystemAccessRequest&amp;)" remove="all"/>
   </value-type>
 
+  <value-type name="QWebEngineFrame" default-constructor="defaultConstructedWebEngineFrame()"
+              since="6.8">
+     <extra-includes>
+         <include file-name="glue/webenginepage_functors.h" location="global"/>
+         <include file-name="glue/webengineframe.h" location="global"/>
+     </extra-includes>
+    <modify-function signature="^runJavaScript\(.*\)$" remove="yes"/>
+    <add-function signature="runJavaScript(const QString &amp;@scriptSource@,PyCallable*@resultCallback@)">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-runjavascript-2"/>
+    </add-function>
+    <add-function signature="runJavaScript(const QString &amp;@scriptSource@,quint32@worldId@,PyCallable*@resultCallback@={})">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-runjavascript-3"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-runjavascript"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-async-note"/>
+    </add-function>
+    <add-function signature="printToPdf(PyCallable*@resultCallback@)">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebengineframe-printtopdf"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebengineframe-printtopdf"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-async-note"/>
+    </add-function>
+  </value-type>
+
   <value-type name="QWebEngineFullScreenRequest"/>
 
   <object-type name="QWebEngineHistory"/>
   <object-type name="QWebEngineNotification"/>
 
   <object-type name="QWebEnginePage">
+     <extra-includes>
+         <include file-name="glue/webenginepage_functors.h" location="global"/>
+     </extra-includes>
     <enum-type name="LifecycleState"/>
     <enum-type name="WebAction"/>
     <enum-type name="FindFlag" flags="FindFlags"/>
       <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
                    snippet="qwebenginepage-javascriptprompt-return"/>
     </modify-function>
-    <add-function signature="findText(const QString &amp;,QWebEnginePage::FindFlags,PyObject*)">
-        <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-findtext"/>
+    <add-function signature="findFrameByName(QString@name@)" return-type="QWebEngineFrame">
+       <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                    snippet="qwebenginepage-findframebyname"/>
+       <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                             snippet="qwebenginepage-findframebyname"/>
     </add-function>
-    <add-function signature="toPlainText(PyObject*) const">
-        <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-convertto"/>
+    <add-function signature="findText(const QString &amp;@subString@,QWebEnginePage::FindFlags@options@,PyCallable*@resultCallback@)">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-findtext"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-findtext"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-async-note"/>
     </add-function>
-    <add-function signature="toHtml(PyObject*) const">
-        <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-convertto"/>
+    <add-function signature="toPlainText(PyCallable*@resultCallback@) const">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-convertto"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-toplaintext"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-async-note"/>
     </add-function>
-    <add-function signature="runJavaScript(const QString &amp;,quint32,PyObject*)">
-        <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-runjavascript"/>
+    <add-function signature="toHtml(PyCallable*@resultCallback@) const">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-convertto"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-tohtml"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-async-note"/>
+    </add-function>
+    <modify-function signature="^runJavaScript\(.*\)$" remove="yes"/>
+    <add-function signature="runJavaScript(const QString &amp;@scriptSource@,PyCallable*@resultCallback@)">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-runjavascript-2"/>
+    </add-function>
+    <add-function signature="runJavaScript(const QString &amp;@scriptSource@,quint32@worldId@=0,PyCallable*@resultCallback@={})">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-runjavascript-3"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-runjavascript"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-async-note"/>
+    </add-function>
+    <add-function signature="printToPdf(PyCallable*@resultCallback@,const QPageLayout &amp;@pageLayout@=QPageLayout(QPageSize(QPageSize::A4),QPageLayout::Portrait,QMarginsF()),const QPageRanges &amp;@ranges@={})">
+        <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                     snippet="qwebenginepage-printtopdf"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-printtopdf"/>
+        <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+                              snippet="qwebenginepage-async-note"/>
     </add-function>
   </object-type>
 
+  <value-type name="QWebEnginePermission" since="6.8">
+    <enum-type name="PermissionType"/>
+    <enum-type name="State"/>
+  </value-type>
+
   <object-type name="QWebEngineProfile">
     <extra-includes>
         <include file-name="QtWebEngineCore/QWebEngineNotification" location="global"/>
                  snippet="qwebengineprofile-functor"/>
     <enum-type name="HttpCacheType"/>
     <enum-type name="PersistentCookiesPolicy"/>
+    <enum-type name="PersistentPermissionsPolicy" since="6.8"/>
     <add-function signature="setNotificationPresenter(PyCallable* @notificationPresenter@)">
       <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp" snippet="qwebengineprofile-setnotificationpresenter"/>
     </add-function>
   <object-type name="QWebEngineSettings">
     <enum-type name="FontFamily"/>
     <enum-type name="FontSize"/>
+    <enum-type name="ImageAnimationPolicy" since="6.8"/>
     <enum-type name="UnknownUrlSchemePolicy"/>
     <enum-type name="WebAttribute"/>
+
   </object-type>
 
   <object-type name="QWebEngineHttpRequest">
 
   <object-type name="QWebEngineUrlSchemeHandler"/>
 
-  <!-- FIXME  not in snapshot yet
-  <value-type name="QWebEngineDesktopMediaRequest" since="6.7"/>
-  <object-type name="QWebEngineMediaSourceModel" since="6.7"/>
-  -->
+  <!-- No default constructor. Used in signal QWebEnginePage.desktopMediaRequested(),
+       but not in any other function. -->
+  <value-type name="QWebEngineDesktopMediaRequest" since="6.7">
+      <modify-function signature="swap(QWebEngineDesktopMediaRequest&amp;)" remove="yes"/>
+  </value-type>
   <value-type name="QWebEngineWebAuthPinRequest" since="6.7"/>
   <object-type name="QWebEngineWebAuthUxRequest" since="6.7">
       <enum-type name="WebAuthUxState"/>
index 2e7d223349db4c27769777393ae23199516fb9e6..3d383337b4e0bb4c0b56b394c0d77dfd4b0d7162 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2021 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtWebEngineQuick"
+<typesystem package="PySide6.QtWebEngineQuick" doc-package="PySide6.QtWebEngine"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
     <load-typesystem name="QtQml/typesystem_qml.xml" generate="no"/>
     <load-typesystem name="QtWebEngineCore/typesystem_webenginecore.xml" generate="no"/>
@@ -13,6 +13,7 @@
     <object-type name="QQuickWebEngineProfile">
         <enum-type name="HttpCacheType"/>
         <enum-type name="PersistentCookiesPolicy"/>
+        <enum-type name="PersistentPermissionsPolicy" since="6.8"/>
     </object-type>
 
 </typesystem>
index 9da97243c8fa5723757b4e5a573452f4430417de..919db570bc4d4eb7ade04f2d4db99e4d7a01c591 100644 (file)
@@ -40,6 +40,3 @@ create_pyside_module(NAME QtWebEngineWidgets
                      DEPS QtWebEngineWidgets_deps
                      TYPESYSTEM_PATH QtWebEngineWidgets_SOURCE_DIR
                      SOURCES QtWebEngineWidgets_SRC)
-
-
-
index 61874856b534401b44c1839600e8e260936f93ff..206efb68e41a559c36082813feda155badf28419 100644 (file)
@@ -3,7 +3,7 @@
 // Copyright (C) 2016 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 -->
-<typesystem package="PySide6.QtWebEngineWidgets"
+<typesystem package="PySide6.QtWebEngineWidgets" doc-package="PySide6.QtWebEngine"
             namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
   <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
   <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
@@ -13,8 +13,9 @@
   <load-typesystem name="QtPrintSupport/typesystem_printsupport.xml" generate="no"/>
 
   <object-type name="QWebEngineView">
-      <add-function signature="findText(const QString &amp;,QWebEnginePage::FindFlags,PyObject*)">
-          <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-findtext"/>
+      <add-function signature="findText(const QString &amp;@subString@,QWebEnginePage::FindFlags@options@,PyCallable*@resultCallback@)">
+          <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+                       snippet="qwebenginepage-findtext"/>
       </add-function>
   </object-type>
 
diff --git a/sources/pyside6/PySide6/QtWebView/CMakeLists.txt b/sources/pyside6/PySide6/QtWebView/CMakeLists.txt
new file mode 100644 (file)
index 0000000..158c720
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+project(QtWebView)
+
+set(QtWebView_Src
+${QtWebView_GEN_DIR}/qtwebview_wrapper.cpp
+# module is always needed
+${QtWebView_GEN_DIR}/qtwebview_module_wrapper.cpp
+)
+
+set(QtWebView_include_dirs ${QtWebView_SOURCE_DIR}
+                           ${QtWebView_BINARY_DIR}
+                           ${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
+                           ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+                           ${Qt${QT_MAJOR_VERSION}WebView_INCLUDE_DIRS}
+                           ${libpyside_SOURCE_DIR}
+                           ${QtGui_GEN_DIR}
+                           ${QtCore_GEN_DIR}
+                           ${QtWebView_GEN_DIR})
+
+set(QtWebView_libraries pyside6
+                        ${Qt${QT_MAJOR_VERSION}WebView_LIBRARIES})
+
+set(QtWebView_deps QtGui)
+
+# for Windows and Linux, QtWebView depends on QtWebEngine to render content
+if ((WIN32 OR UNIX) AND NOT APPLE)
+    list(APPEND QtWebView_deps QtWebEngineCore QtWebEngineQuick)
+endif()
+
+create_pyside_module(NAME QtWebView
+                     INCLUDE_DIRS QtWebView_include_dirs
+                     LIBRARIES QtWebView_libraries
+                     DEPS QtWebView_deps
+                     TYPESYSTEM_PATH QtWebView_SOURCE_DIR
+                     SOURCES QtWebView_Src)
diff --git a/sources/pyside6/PySide6/QtWebView/typesystem_webview.xml b/sources/pyside6/PySide6/QtWebView/typesystem_webview.xml
new file mode 100644 (file)
index 0000000..1983ed8
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+-->
+<typesystem package="PySide6.QtWebView"
+            namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
+    <load-typesystem name="QtCore/typesystem_core.xml" generate ="no"/>
+    <namespace-type name="QtWebView"/>
+</typesystem>
index 0669c18f65022a63335dcab2e0218be50c467cdd..7879cdebd1b4971b4a93f33ad9e30e426d318914 100644 (file)
     </modify-function>
   </object-type>
   <object-type name="QAbstractButton">
-    <add-function signature="setShortcut(Qt::Key)">
+    <add-function signature="setShortcut(Qt::Key@key@)">
       <inject-code file="../glue/qtgui.cpp" snippet="set-qtkey-shortcut"/>
     </add-function>
   </object-type>
     <modify-function signature="getColor(const QColor&amp;,QWidget*,const QString&amp;,QFlags&lt;QColorDialog::ColorDialogOption>)" allow-thread="yes"/>
   </object-type>
 
-  <object-type name="QLayout">
+  <object-type name="QLayout" polymorphic-base="true">
     <inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp"
                  snippet="qwidget-retrieveobjectname"/>
     <inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp" snippet="qlayout-help-functions"/>
         </inject-code>
     </modify-function>
 
-    <add-function signature="setAlignment(QFlags&lt;Qt::AlignmentFlag&gt;)">
+    <add-function signature="setAlignment(QFlags&lt;Qt::AlignmentFlag&gt;@alignment@)">
       <inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp" snippet="qlayout-setalignment"/>
     </add-function>
 
     <modify-function signature="removeItemWidget(QListWidgetItem*)" allow-thread="yes"/>
   </object-type>
 
-  <object-type name="QWidget" delete-in-main-thread="true">
+  <object-type name="QWidget" delete-in-main-thread="true" polymorphic-base="true">
     <!-- see QWindow::nativeEvent(), QAbstractNativeEventFilter::nativeEventFilter() -->
     <inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp" snippet="qwidget-addaction-glue"/>
     <inject-code class="native" position="beginning">
     </modify-function>
 
     <modify-function signature="parentWidget()const">
-        <modify-argument index="this">
-          <parent index="return" action="add"/>
-        </modify-argument>
         <modify-argument index="return">
           <define-ownership class="target" owner="default"/>
         </modify-argument>
     </modify-function>
     <extra-includes>
       <include file-name="QPixmap" location="global"/>
+      <include file-name="qobjectconnect.h" location="global"/>
     </extra-includes>
-
+    <add-function signature="open(PyCallable*@functor@)">
+        <inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp"
+                     snippet="qmessagebox-open-connect-accept"/>
+    </add-function>
     <!-- FIXME PYSIDE-7: Remove deprecated overloads -->
     <modify-function signature="critical(QWidget*,const QString&amp;,const QString&amp;,QFlags&lt;QMessageBox::StandardButton&gt;,QMessageBox::StandardButton)" allow-thread="yes"/>
     <modify-function signature="critical(QWidget*,const QString&amp;,const QString&amp;,QMessageBox::StandardButton,QMessageBox::StandardButton)"
 
     <modify-function signature="pixmap(Qt::ReturnByValueConstant)const" remove="all"/>
     <modify-function signature="picture(Qt::ReturnByValueConstant)const" remove="all"/>
+    <!-- // FIXME PYSIDE7: PYSIDE-2808 Remove (resource cleanup)? -->
     <modify-function signature="setMovie(QMovie *)">
       <modify-argument index="1">
         <parent index="this" action="add"/>
     </modify-function>
     <modify-function signature="del()" rename="del_"/>
 
-    <add-function signature="addAction(QAction*)">
+    <add-function signature="addAction(QAction*@action@)">
       <inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp" snippet="qlineedit-addaction"/>
     </add-function>
 
       <include file-name="pysidecleanup.h" location="global"/>
     </extra-includes>
     <modify-function signature="QApplication(int&amp;,char**,int)" access="private"/>
-    <add-function signature="QApplication(QStringList)">
+    <add-function signature="QApplication(QStringList@arguments@)">
         <inject-code file="../glue/qtwidgets.cpp" snippet="qapplication-1"/>
     </add-function>
     <add-function signature="QApplication()">
     </value-type>
 
     <modify-function signature="getLayoutPosition(QLayout*,int*,QFormLayout::ItemRole*)const">
-        <modify-argument index="0">
-            <replace-type modified-type="PyObject"/>
-        </modify-argument>
         <modify-argument index="2">
             <remove-argument/>
         </modify-argument>
         <modify-argument index="3">
             <remove-argument/>
         </modify-argument>
+        <modify-argument index="return" pyi-type="Tuple[int, PySide6.QtWidgets.QFormLayout.ItemRole]">
+            <replace-type modified-type="PyTuple"/>
+        </modify-argument>
         <inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp" snippet="qformlayout-fix-args" />
     </modify-function>
     <modify-function signature="getWidgetPosition(QWidget*,int*,QFormLayout::ItemRole*)const">
index 3661a67bfb0d984f3c471a67669fb0ffd01792fe..089978b6d30f2aad135cb2294c44afc7a0a92240 100644 (file)
     </value-type>
 
 </typesystem>
-
index d0a4ecc3700868858c0c706e4085fc04f8a3d81d..45c19f2e906243f32609ba30b7518a6f5692865a 100644 (file)
@@ -1,15 +1,16 @@
 import os
 import sys
 from pathlib import Path
-from textwrap import dedent
+from types import ModuleType
+# mypy: disable-error-code="name-defined"
 
-# __all__ is also corrected below.
-__all__ = list("Qt" + body for body in
-    "@all_module_shortnames@"
-    .split(";"))
+# __all__ is computed below.
+__pre_all__ = [@init_modules@]
 __version__ = "@FINAL_PACKAGE_VERSION@"
 __version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", "@BINDING_API_PRE_RELEASE_VERSION@")
 
+SKIP_MYPY_TEST = bool("@SKIP_MYPY_TEST@")
+
 
 def _additional_dll_directories(package_dir):
     # Find shiboken6 relative to the package directory.
@@ -18,7 +19,7 @@ def _additional_dll_directories(package_dir):
     if root.suffix == '.zip':
         return []
     shiboken6 = root / 'shiboken6'
-    if shiboken6.is_dir(): # Standard case, only shiboken6 is needed
+    if shiboken6.is_dir():  # Standard case, only shiboken6 is needed
         return [shiboken6]
     # The below code is for the build process when generate_pyi.py
     # is executed in the build directory. We need libpyside and Qt in addition.
@@ -31,9 +32,9 @@ def _additional_dll_directories(package_dir):
         result.append(libpysideqml)
     for path in os.environ.get('PATH').split(';'):
         if path:
-             if (Path(path) / 'qmake.exe').exists():
-                 result.append(path)
-                 break
+            if (Path(path) / 'qmake.exe').exists():
+                result.append(path)
+                break
     return result
 
 
@@ -54,13 +55,14 @@ def _setupQtDirectories():
 
     pyside_package_dir = Path(__file__).parent.resolve()
 
-    if sys.platform == 'win32' and sys.version_info[0] == 3 and sys.version_info[1] >= 8:
+    if sys.platform == 'win32':
         for dir in _additional_dll_directories(pyside_package_dir):
             os.add_dll_directory(os.fspath(dir))
 
     try:
         # PYSIDE-1497: we use the build dir or install dir or site-packages, whatever the path
         #              setting dictates. There is no longer a difference in path structure.
+        global Shiboken
         from shiboken6 import Shiboken
     except Exception:
         paths = ', '.join(sys.path)
@@ -97,20 +99,13 @@ def _find_all_qt_modules():
     # Since the wheel split, the __all__ variable cannot be computed statically,
     # because we don't know all modules in advance.
 
-    # Instead, we look into the file system and quickly build a list of all
-    # existing .pyi files, because importing is not desired and also impossible during import.
-    # By using the initially created list, we can keep some order intact.
+    # Instead, we use __getattr__ which is supported since Python 3.7
+    # and create the __all__ list on demand when needed.
     location = Path(__file__).resolve().parent
-
-    # Note: We should _not_ call this function while still building, but use the existing value!
-    in_build = Path("@CMAKE_BINARY_DIR@") in location.parents
-
-    if in_build:
-        return __all__
-
     files = os.listdir(location)
-    unordered = set(name[:-4] for name in files if name.startswith("Qt") and name.endswith(".pyi"))
-    ordered_part = __all__
+    unordered = set(name[: name.find(".")] for name in files if name.startswith("Qt") and (
+                                                                name.endswith((".pyd", ".so"))))
+    ordered_part = __pre_all__
     result = []
     for name in ordered_part:
         if name in unordered:
@@ -120,5 +115,27 @@ def _find_all_qt_modules():
     return result
 
 
-__all__ = _find_all_qt_modules()
+# Provide the __all__ variable only on access.
+def __getattr__(name: str) -> list[str]:
+    if name == "__all__":
+        global __all__
+        __all__ = _find_all_qt_modules()
+        return __all__
+    raise AttributeError(f"module '{__name__}' has no attribute '{name}' :)")
+
+
+# Be prepared that people can access the module dict instead.
+class ModuleDict(dict):
+    def __missing__(self, key):
+        if key == "__all__":
+            self[key] = __all__ if "__all__" in globals() else __getattr__("__all__")
+            return __all__
+        raise KeyError(f"dict of module '{__name__}' has no key '{key}' :)")
+
+
+class SubModule(ModuleType):
+    pass
+
+
 _setupQtDirectories()
+Shiboken.replaceModuleDict(sys.modules["PySide6"], SubModule, ModuleDict(globals()))
diff --git a/sources/pyside6/PySide6/doc/qtcore.rst b/sources/pyside6/PySide6/doc/qtcore.rst
new file mode 100644 (file)
index 0000000..35ba0d1
--- /dev/null
@@ -0,0 +1,95 @@
+// @snippet q_arg
+This function takes a type (or a type string) and a value of that type
+and returns an internal object that can be passed to
+:meth:`QMetaObject.invokeMethod`. See also Q_RETURN_ARG().
+// @snippet q_arg
+
+// @snippet q_return_arg
+This macro takes a type (or a type string) a value of which is then
+returned by :meth:`QMetaObject.invokeMethod`. See also Q_ARG().
+// @snippet q_return_arg
+
+// @snippet qlocale-system
+Returns a QLocale object initialized to the system locale.
+
+The system locale may use system-specific sources for locale data, where
+available, otherwise falling back on QLocale's built-in database entry for the
+language, script and territory the system reports.
+
+For example, on Windows, this locale will use the decimal/grouping characters and
+date/time formats specified in the system configuration panel.
+
+.. note:: Qt for Python on macOS will not reflect the user's region and language
+          preferences though QLocale::system(), but will instead reflect the
+          environment variables POSIX uses to specify locale, similar to Python's
+          locale module. If the system locale cannot be determined, which can be
+          due to none of the variables 'LC_ALL', 'LC_CTYPE', 'LANG' or 'LANGUAGE'
+          being set by your environment, then the default POSIX locale or
+          'C' locale is returned.
+
+See also c().
+// @snippet qlocale-system
+
+// @snippet qabstractitemmodel-createindex
+Creates a model index for the given row and column with the internal pointer
+ptr. When using a :class:`QSortFilterProxyModel`, its indexes have their own
+internal pointer. It is not advisable to access this internal pointer outside
+of the model. Use the ``data()`` function instead.
+
+This function provides a consistent interface that model subclasses must use to
+create model indexes.
+
+.. warning:: Because of some Qt/Python integration rules, the ``ptr`` argument does
+             not get the reference incremented during the QModelIndex life time.
+             So it is necessary to keep the object used on ``ptr`` argument alive
+             during the whole process. Do not destroy the object if you are not
+             sure about that.
+// @snippet qabstractitemmodel-createindex
+
+// @snippet qobject-findChild
+To find the child of a certain :class:`QObject`, the first argument of this
+function should be the child's type, and the second the name of the child:
+
+::
+
+    ...
+    parent = QWidget()
+    ...
+    # The first argument must be the child type
+    child1 = parent.findChild(QPushButton, "child_button")
+    child2 = parent.findChild(QWidget, "child_widget")
+
+// @snippet qobject-findChild
+
+// @snippet qcoreapplication-init
+Constructs a Qt kernel application. Kernel applications are applications
+without a graphical user interface. These type of applications are used
+at the console or as server processes.
+
+The *args* argument is processed by the application, and made available
+in a more convenient form by the :meth:`~PySide6.QtCore.QCoreApplication.arguments()`
+method.
+// @snippet qcoreapplication-init
+
+// @snippet qsettings-value
+Custom overload that adds an optional named parameter to the function ``value()``
+to automatically cast the type that is being returned by the function.
+
+An example of this situation could be an ini file that contains
+the value of a one-element list::
+
+    settings.setValue('var', ['a'])
+
+The the ini file will be::
+
+    [General]
+    var=a  # we cannot know that this is a list!
+
+Once we read it, we could specify if we want
+the default behavior, a str, or to cast the output
+to a list.
+
+    settings.value('var')  # Will get "a"
+    settings.value('var', type=list)  # Will get ["a"]
+
+// @snippet qsettings-value
diff --git a/sources/pyside6/PySide6/doc/qtqml.rst b/sources/pyside6/PySide6/doc/qtqml.rst
new file mode 100644 (file)
index 0000000..31801b2
--- /dev/null
@@ -0,0 +1,152 @@
+// @snippet qmlregistersingletoninstance
+.. py:function:: qmlRegisterSingletonInstance(pytype: type,\
+                                              uri: str,\
+                                              versionMajor: int,\
+                                              versionMinor: int,\
+                                              typeName: str,\
+                                              instanceObject: object) -> int
+
+   :param type pytype: Python class
+   :param str uri: uri to use while importing the component in QML
+   :param int versionMajor: major version
+   :param int versionMinor: minor version
+   :param str typeName: name exposed to QML
+   :param object instanceObject: singleton object to be registered
+   :return: int (the QML type id)
+
+This function registers a singleton Python object *instanceObject*, with a
+particular *uri* and *typeName*. Its version is a combination of *versionMajor*
+and *versionMinor*. Use this function to register an object of the given type
+*pytype* as a singleton type.
+// @snippet qmlregistersingletoninstance
+
+// @snippet qmlregistersingletontype_qobject_nocallback
+.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str) -> int
+
+   :param type pytype: Python class
+   :param str uri: uri to use while importing the component in QML
+   :param int versionMajor: major version
+   :param int versionMinor: minor version
+   :param str typeName: name exposed to QML
+   :return: int (the QML type id)
+
+This function registers a Python type as a singleton in the QML system.
+
+Alternatively, the :ref:`QmlSingleton` decorator can be used.
+// @snippet qmlregistersingletontype_qobject_nocallback
+
+// @snippet qmlregistersingletontype_qobject_callback
+.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
+
+   :param type pytype: Python class
+   :param str uri: uri to use while importing the component in QML
+   :param int versionMajor: major version
+   :param int versionMinor: minor version
+   :param str typeName: name exposed to QML
+   :param object callback: Python callable (to handle Python type)
+   :return: int (the QML type id)
+
+This function registers a Python type as a singleton in the QML system using
+the provided callback (which gets a QQmlEngine as a parameter) to generate the
+singleton.
+// @snippet qmlregistersingletontype_qobject_callback
+
+// @snippet qmlregistersingletontype_qjsvalue
+.. py:function:: qmlRegisterSingletonType(uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
+
+   :param str uri: uri to use while importing the component in QML
+   :param int versionMajor: major version
+   :param int versionMinor: minor version
+   :param str typeName: name exposed to QML
+   :param object callback: Python callable (to handle QJSValue)
+   :return: int (the QML type id)
+
+This function registers a QJSValue as a singleton in the QML system using the
+provided callback (which gets a QQmlEngine as a parameter) to generate the
+singleton.
+// @snippet qmlregistersingletontype_qjsvalue
+
+// @snippet qmlregistertype
+.. py:function:: qmlRegisterType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str) -> int
+
+   :param type pytype: Python class
+   :param str uri: uri to use while importing the component in QML
+   :param int versionMajor: major version
+   :param int versionMinor: minor version
+   :param str qmlName: name exposed to QML
+   :return: int (the QML type id)
+
+This function registers the Python *type* in the QML system with the name
+*qmlName*, in the library imported from *uri* having the version number
+composed from *versionMajor* and *versionMinor*. For example, this registers a
+Python class 'MySliderItem' as a QML type named 'Slider' for version '1.0' of a
+module called 'com.mycompany.qmlcomponents':
+
+   ::
+
+       qmlRegisterType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider")
+
+Once this is registered, the type can be used in QML by importing the specified
+module name and version number:
+
+   ::
+
+       import com.mycompany.qmlcomponents 1.0
+
+       Slider { ... }
+
+Note that it's perfectly reasonable for a library to register types to older
+versions than the actual version of the library. Indeed, it is normal for the
+new library to allow QML written to previous versions to continue to work, even
+if more advanced versions of some of its types are available.
+// @snippet qmlregistertype
+
+// @snippet qmlregisteruncreatabletype
+.. py:function:: qmlRegisterUncreatableType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str, noCreationReason: str) -> int
+
+   :param type pytype: Python class
+   :param str uri: uri to use while importing the component in QML
+   :param int versionMajor: major version
+   :param int versionMinor: minor version
+   :param str qmlName: name exposed to QML
+   :param str noCreationReason: Error message shown when trying to create the QML type
+   :return: int (the QML type id)
+
+This function registers the Python *type* in the QML system as an uncreatable
+type with the name *qmlName*, in the library imported from *uri* having the
+version number composed from *versionMajor* and *versionMinor*, showing
+*noCreationReason* as an error message when creating the type is attempted. For
+example, this registers a Python class 'MySliderItem' as a QML type named
+'Slider' for version '1.0' of a module called 'com.mycompany.qmlcomponents':
+
+   ::
+       qmlRegisterUncreatableType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider", "Slider cannot be created.")
+
+Note that it's perfectly reasonable for a library to register types to older
+versions than the actual version of the library. Indeed, it is normal for the
+new library to allow QML written to previous versions to continue to work, even
+if more advanced versions of some of its types are available.
+
+Alternatively, the :ref:`QmlUncreatable` decorator can be used.
+// @snippet qmlregisteruncreatabletype
+
+// @snippet qqmlengine-singletoninstance-qmltypeid
+Returns the instance of a singleton type that was registered under qmlTypeId.
+For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
+otherwise a ``QJSValue`` or ``None``.
+
+It is recommended to store the QML type id, e.g. as a static member in the
+singleton class. The lookup via qmlTypeId() is costly.
+// @snippet qqmlengine-singletoninstance-qmltypeid
+
+// @snippet qqmlengine-singletoninstance-typename Returns the instance of a
+singleton type named typeName from the module specified by uri.
+For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
+otherwise a ``QJSValue`` or ``None``.
+
+This method can be used as an alternative to calling qmlTypeId followed by the
+id based overload of singletonInstance. This is convenient when one only needs
+to do a one time setup of a singleton; if repeated access to the singleton is
+required, caching its typeId will allow faster subsequent access via the
+type-id based overload.
+// @snippet qqmlengine-singletoninstance-typename
diff --git a/sources/pyside6/PySide6/doc/qtqml_functions.rst b/sources/pyside6/PySide6/doc/qtqml_functions.rst
deleted file mode 100644 (file)
index 31801b2..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-// @snippet qmlregistersingletoninstance
-.. py:function:: qmlRegisterSingletonInstance(pytype: type,\
-                                              uri: str,\
-                                              versionMajor: int,\
-                                              versionMinor: int,\
-                                              typeName: str,\
-                                              instanceObject: object) -> int
-
-   :param type pytype: Python class
-   :param str uri: uri to use while importing the component in QML
-   :param int versionMajor: major version
-   :param int versionMinor: minor version
-   :param str typeName: name exposed to QML
-   :param object instanceObject: singleton object to be registered
-   :return: int (the QML type id)
-
-This function registers a singleton Python object *instanceObject*, with a
-particular *uri* and *typeName*. Its version is a combination of *versionMajor*
-and *versionMinor*. Use this function to register an object of the given type
-*pytype* as a singleton type.
-// @snippet qmlregistersingletoninstance
-
-// @snippet qmlregistersingletontype_qobject_nocallback
-.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str) -> int
-
-   :param type pytype: Python class
-   :param str uri: uri to use while importing the component in QML
-   :param int versionMajor: major version
-   :param int versionMinor: minor version
-   :param str typeName: name exposed to QML
-   :return: int (the QML type id)
-
-This function registers a Python type as a singleton in the QML system.
-
-Alternatively, the :ref:`QmlSingleton` decorator can be used.
-// @snippet qmlregistersingletontype_qobject_nocallback
-
-// @snippet qmlregistersingletontype_qobject_callback
-.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
-
-   :param type pytype: Python class
-   :param str uri: uri to use while importing the component in QML
-   :param int versionMajor: major version
-   :param int versionMinor: minor version
-   :param str typeName: name exposed to QML
-   :param object callback: Python callable (to handle Python type)
-   :return: int (the QML type id)
-
-This function registers a Python type as a singleton in the QML system using
-the provided callback (which gets a QQmlEngine as a parameter) to generate the
-singleton.
-// @snippet qmlregistersingletontype_qobject_callback
-
-// @snippet qmlregistersingletontype_qjsvalue
-.. py:function:: qmlRegisterSingletonType(uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
-
-   :param str uri: uri to use while importing the component in QML
-   :param int versionMajor: major version
-   :param int versionMinor: minor version
-   :param str typeName: name exposed to QML
-   :param object callback: Python callable (to handle QJSValue)
-   :return: int (the QML type id)
-
-This function registers a QJSValue as a singleton in the QML system using the
-provided callback (which gets a QQmlEngine as a parameter) to generate the
-singleton.
-// @snippet qmlregistersingletontype_qjsvalue
-
-// @snippet qmlregistertype
-.. py:function:: qmlRegisterType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str) -> int
-
-   :param type pytype: Python class
-   :param str uri: uri to use while importing the component in QML
-   :param int versionMajor: major version
-   :param int versionMinor: minor version
-   :param str qmlName: name exposed to QML
-   :return: int (the QML type id)
-
-This function registers the Python *type* in the QML system with the name
-*qmlName*, in the library imported from *uri* having the version number
-composed from *versionMajor* and *versionMinor*. For example, this registers a
-Python class 'MySliderItem' as a QML type named 'Slider' for version '1.0' of a
-module called 'com.mycompany.qmlcomponents':
-
-   ::
-
-       qmlRegisterType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider")
-
-Once this is registered, the type can be used in QML by importing the specified
-module name and version number:
-
-   ::
-
-       import com.mycompany.qmlcomponents 1.0
-
-       Slider { ... }
-
-Note that it's perfectly reasonable for a library to register types to older
-versions than the actual version of the library. Indeed, it is normal for the
-new library to allow QML written to previous versions to continue to work, even
-if more advanced versions of some of its types are available.
-// @snippet qmlregistertype
-
-// @snippet qmlregisteruncreatabletype
-.. py:function:: qmlRegisterUncreatableType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str, noCreationReason: str) -> int
-
-   :param type pytype: Python class
-   :param str uri: uri to use while importing the component in QML
-   :param int versionMajor: major version
-   :param int versionMinor: minor version
-   :param str qmlName: name exposed to QML
-   :param str noCreationReason: Error message shown when trying to create the QML type
-   :return: int (the QML type id)
-
-This function registers the Python *type* in the QML system as an uncreatable
-type with the name *qmlName*, in the library imported from *uri* having the
-version number composed from *versionMajor* and *versionMinor*, showing
-*noCreationReason* as an error message when creating the type is attempted. For
-example, this registers a Python class 'MySliderItem' as a QML type named
-'Slider' for version '1.0' of a module called 'com.mycompany.qmlcomponents':
-
-   ::
-       qmlRegisterUncreatableType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider", "Slider cannot be created.")
-
-Note that it's perfectly reasonable for a library to register types to older
-versions than the actual version of the library. Indeed, it is normal for the
-new library to allow QML written to previous versions to continue to work, even
-if more advanced versions of some of its types are available.
-
-Alternatively, the :ref:`QmlUncreatable` decorator can be used.
-// @snippet qmlregisteruncreatabletype
-
-// @snippet qqmlengine-singletoninstance-qmltypeid
-Returns the instance of a singleton type that was registered under qmlTypeId.
-For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
-otherwise a ``QJSValue`` or ``None``.
-
-It is recommended to store the QML type id, e.g. as a static member in the
-singleton class. The lookup via qmlTypeId() is costly.
-// @snippet qqmlengine-singletoninstance-qmltypeid
-
-// @snippet qqmlengine-singletoninstance-typename Returns the instance of a
-singleton type named typeName from the module specified by uri.
-For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
-otherwise a ``QJSValue`` or ``None``.
-
-This method can be used as an alternative to calling qmlTypeId followed by the
-id based overload of singletonInstance. This is convenient when one only needs
-to do a one time setup of a singleton; if repeated access to the singleton is
-required, caching its typeId will allow faster subsequent access via the
-type-id based overload.
-// @snippet qqmlengine-singletoninstance-typename
diff --git a/sources/pyside6/PySide6/doc/qtwebenginecore.rst b/sources/pyside6/PySide6/doc/qtwebenginecore.rst
new file mode 100644 (file)
index 0000000..75e2e32
--- /dev/null
@@ -0,0 +1,75 @@
+// @snippet qwebenginepage-async-note
+.. note:: We guarantee that the ``resultCallback`` is always called, but it
+          might be done during page destruction. When ``QWebEnginePage``
+          is deleted, the callback is triggered with an invalid value and it
+          is not safe to use the corresponding ``QWebEnginePage``,
+          ``QWebEngineFrame``, or ``QWebEngineView`` instance inside it.
+// @snippet qwebenginepage-async-note
+
+// @snippet qwebenginepage-findtext
+Finds the specified string, ``subString``, in the page, using the given
+``options``. The ``findTextFinished()`` signal is emitted when a string search
+is completed.
+
+To clear the search highlight, just pass an empty string.
+
+The ``resultCallback`` must take a ``QWebEngineFindTextResult`` parameter.
+// @snippet qwebenginepage-findtext
+
+// @snippet qwebenginepage-tohtml
+Asynchronous method to retrieve the page's content as HTML, enclosed in HTML
+and BODY tags. Upon successful completion, ``resultCallback`` is called with
+the page's content.
+// @snippet qwebenginepage-tohtml
+
+// @snippet qwebenginepage-toplaintext
+Asynchronous method to retrieve the page's content converted to plain text,
+completely stripped of all HTML formatting.
+
+Upon successful completion, ``resultCallback`` is called with the page's content.
+// @snippet qwebenginepage-toplaintext
+
+// @snippet qwebenginepage-runjavascript
+Runs the JavaScript code contained in ``scriptSource`` script on this frame,
+without checking whether the DOM of the page has been constructed.
+
+To avoid conflicts with other scripts executed on the page, the world in which
+the script is run is specified by ``worldId``. The world ID values are the same
+as provided by ``QWebEngineScript.ScriptWorldId``, and between 0 and 256. If
+you leave out the world ID, the script is run in the ``MainWorld`` (0).
+
+When the script has been executed, the callable ``resultCallback`` is called
+with the result of the last executed statement.
+
+Only plain data can be returned from JavaScript as the result value.
+
+.. note:: Do not execute lengthy routines in the callback function, because
+          it might block the rendering of the web engine page.
+// @snippet qwebenginepage-runjavascript
+
+// @snippet qwebenginepage-printtopdf
+Renders the current content of the page into a PDF document and returns a byte
+array containing the PDF data as parameter to ``resultCallback``.
+
+The page size and orientation of the produced PDF document are taken from the
+values specified in ``layout``, while the range of pages printed is taken from
+``ranges`` with the default being printing all pages.
+
+.. note:: The ``QWebEnginePage.WebAction.Stop`` web action can be used to
+          interrupt this operation.
+// @snippet qwebenginepage-printtopdf
+
+// @snippet qwebenginepage-findframebyname
+Returns the frame with the given ``name``. If there are multiple frames with
+the same name, which one is returned is arbitrary. If no frame was found,
+returns ``None``.
+// @snippet qwebenginepage-findframebyname
+
+// @snippet qwebengineframe-printtopdf
+Renders the current content of the frame into a PDF document and returns a byte
+array containing the PDF data as parameter to ``resultCallback``. Printing uses
+a page size of A4, portrait layout, and includes the full range of pages.
+
+.. note:: The ``QWebEnginePage.WebAction.Stop`` web action can be used to
+          interrupt this operation.
+// @snippet qwebengineframe-printtopdf
index b6e485e2cdcf97d32d2b55dc462194227f18b38e..dd08a38afc1cb0330faede6ada143557edf004ee 100644 (file)
@@ -15,7 +15,7 @@ bool cppResult = %CPPSELF.%FUNCTION_NAME(rule,
     Shiboken::GilState state;
     auto *requestPtr = &request;
     Shiboken::AutoDecRef arglist(PyTuple_New(1));
-    PyTuple_SET_ITEM(arglist, 0,
+    PyTuple_SetItem(arglist, 0,
                      %CONVERTTOPYTHON[QHttpServerRequest *](requestPtr));
     PyObject *ret = PyObject_CallObject(callable, arglist);
     if (PyErr_Occurred())
@@ -29,22 +29,23 @@ bool cppResult = %CPPSELF.%FUNCTION_NAME(rule,
 %PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
 // @snippet qhttpserver-route
 
-// @snippet qhttpserver-afterrequest
-auto *callable = %PYARG_1;
+// @snippet qhttpserver-addafterrequesthandler
+auto *callable = %PYARG_2;
 
-%CPPSELF.%FUNCTION_NAME([callable](QHttpServerResponse &&response,
-                                   const QHttpServerRequest &request) {
+auto callback = [callable](const QHttpServerRequest &request,
+                           QHttpServerResponse &response) {
     Shiboken::GilState state;
     Shiboken::AutoDecRef arglist(PyTuple_New(2));
     auto *responsePtr = &response;
     auto *requestPtr = &request;
-    PyTuple_SET_ITEM(arglist, 0,
-                     %CONVERTTOPYTHON[QHttpServerResponse *](responsePtr));
-    PyTuple_SET_ITEM(arglist, 1,
+    PyTuple_SetItem(arglist, 0,
                      %CONVERTTOPYTHON[QHttpServerRequest *](requestPtr));
+    PyTuple_SetItem(arglist, 1,
+                     %CONVERTTOPYTHON[QHttpServerResponse *](responsePtr));
     PyObject_CallObject(callable, arglist);
     if (PyErr_Occurred())
         PyErr_Print();
-    return std::move(response);
-});
-// @snippet qhttpserver-afterrequest
+};
+
+%CPPSELF.%FUNCTION_NAME(%1, callback);
+// @snippet qhttpserver-addafterrequesthandler
index 0c206db7265f01bfbe9a780f0e2ebddcb8224e32..c3465682c4c21ef6bba35a607915df1645a0626f 100644 (file)
@@ -231,8 +231,8 @@ static QVariant QVariant_convertToVariantMap(PyObject *map)
     Shiboken::AutoDecRef keys(PyDict_Keys(map));
     if (!QVariant_isStringList(keys))
         return {};
-    PyObject *key;
-    PyObject *value;
+    PyObject *key{};
+    PyObject *value{};
     QMap<QString,QVariant> ret;
     while (PyDict_Next(map, &pos, &key, &value)) {
         QString cppKey = %CONVERTTOCPP[QString](key);
@@ -259,9 +259,9 @@ static QVariant QVariant_convertToVariantList(PyObject *list)
 
     QList<QVariant> lst;
     Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList"));
-    const Py_ssize_t size = PySequence_Fast_GET_SIZE(fast.object());
+    const Py_ssize_t size = PySequence_Size(fast.object());
     for (Py_ssize_t i = 0; i < size; ++i) {
-        PyObject *pyItem = PySequence_Fast_GET_ITEM(fast.object(), i);
+        Shiboken::AutoDecRef pyItem(PySequence_GetItem(fast.object(), i));
         QVariant item = %CONVERTTOCPP[QVariant](pyItem);
         lst.append(item);
     }
@@ -322,7 +322,7 @@ QByteArray uncompressed = %FUNCTION_NAME(ptr, %2);
 QList<QByteArray> version = QByteArray(qVersion()).split('.');
 PyObject *pyQtVersion = PyTuple_New(3);
 for (int i = 0; i < 3; ++i)
-    PyTuple_SET_ITEM(pyQtVersion, i, PyLong_FromLong(version[i].toInt()));
+    PyTuple_SetItem(pyQtVersion, i, PyLong_FromLong(version[i].toInt()));
 PyModule_AddObject(module, "__version_info__", pyQtVersion);
 PyModule_AddStringConstant(module, "__version__", qVersion());
 // @snippet qt-version
@@ -374,7 +374,7 @@ PyModule_AddStringConstant(module, "__version__", qVersion());
 // @snippet qobject-connect-6
 
 // @snippet qobject-emit
-%RETURN_TYPE %0 = PySide::SignalManager::instance().emitSignal(%CPPSELF, %1, %PYARG_2);
+%RETURN_TYPE %0 = PySide::SignalManager::emitSignal(%CPPSELF, %1, %PYARG_2);
 %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
 // @snippet qobject-emit
 
@@ -424,7 +424,6 @@ Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK
 
 PySide::registerInternalQtConf();
 PySide::init(module);
-Py_AtExit(QtCoreModuleExit);
 // @snippet qt-pysideinit
 
 // @snippet qt-messagehandler
@@ -435,17 +434,13 @@ static void msgHandlerCallback(QtMsgType type, const QMessageLogContext &ctx, co
 {
     Shiboken::GilState state;
     Shiboken::AutoDecRef arglist(PyTuple_New(3));
-    PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QtMsgType](type));
-    PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QMessageLogContext &](ctx));
+    PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QtMsgType](type));
+    PyTuple_SetItem(arglist, 1, %CONVERTTOPYTHON[QMessageLogContext &](ctx));
     QByteArray array = msg.toUtf8();  // Python handler requires UTF-8
     const char *data = array.constData();
-    PyTuple_SET_ITEM(arglist, 2, %CONVERTTOPYTHON[const char *](data));
+    PyTuple_SetItem(arglist, 2, %CONVERTTOPYTHON[const char *](data));
     Shiboken::AutoDecRef ret(PyObject_CallObject(qtmsghandler, arglist));
 }
-static void QtCoreModuleExit()
-{
-    PySide::SignalManager::instance().clear();
-}
 // @snippet qt-messagehandler
 
 // @snippet qt-installmessagehandler
@@ -479,8 +474,8 @@ namespace PySide {
 QPointF p;
 %RETURN_TYPE retval = %CPPSELF.%FUNCTION_NAME(%ARGUMENT_NAMES, &p);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QPointF](p));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QPointF](p));
 // @snippet qlinef-intersect
 
 // @snippet qresource-data
@@ -503,23 +498,26 @@ if (!PyDateTimeAPI)
 int year, month, day;
 %CPPSELF.%FUNCTION_NAME(&year, &month, &day);
 %PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](year));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](month));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](day));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](year));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](month));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](day));
 // @snippet qdate-getdate
 
 // @snippet qdate-weeknumber
 int yearNumber;
 int week = %CPPSELF.%FUNCTION_NAME(&yearNumber);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](week));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](yearNumber));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](week));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](yearNumber));
 // @snippet qdate-weeknumber
 
 // @snippet qdatetime-1
 QDate date(%1, %2, %3);
 QTime time(%4, %5, %6, %7);
-%0 = new %TYPE(date, time, Qt::TimeSpec(%8));
+%0 = new %TYPE(date, time,
+               Qt::TimeSpec(%8) == Qt::UTC
+               ? QTimeZone(QTimeZone::UTC) : QTimeZone(QTimeZone::LocalTime));
+Shiboken::Warnings::warnDeprecated("QDateTime", "QDateTime(..., Qt::TimeSpec spec)");
 // @snippet qdatetime-1
 
 // @snippet qdatetime-2
@@ -531,7 +529,9 @@ QTime time(%4, %5, %6);
 // @snippet qdatetime-3
 QDate date(%1, %2, %3);
 QTime time(%4, %5, %6, %7);
-%0 = new %TYPE(date, time, %8);
+%0 = new %TYPE(date, time,
+               %8 == Qt::UTC ? QTimeZone(QTimeZone::UTC) : QTimeZone(QTimeZone::LocalTime));
+Shiboken::Warnings::warnDeprecated("QDateTime", "QDateTime(..., Qt::TimeSpec spec)");
 // @snippet qdatetime-3
 
 // @snippet qdatetime-topython
@@ -553,9 +553,10 @@ return %CPPSELF.size();
 // @snippet qbitarray-len
 
 // @snippet qbitarray-getitem
-if (_i < 0 || _i >= %CPPSELF.size()) {
-    PyErr_SetString(PyExc_IndexError, "index out of bounds");
-    return 0;
+const Py_ssize_t size = %CPPSELF.size();
+if (_i < 0 || _i >= size) {
+    Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+    return nullptr;
 }
 bool ret = %CPPSELF.at(_i);
 return %CONVERTTOPYTHON[bool](ret);
@@ -569,6 +570,26 @@ Py_XDECREF(result);
 return !result ? -1 : 0;
 // @snippet qbitarray-setitem
 
+// @snippet qmodelroledata-setdata
+// Call template <typename T> void QModelRoleData::setData(T &&value)
+%CPPSELF.%FUNCTION_NAME(%1);
+// @snippet qmodelroledata-setdata
+
+// @snippet qmodelroledataspan-len
+return %CPPSELF.size();
+// @snippet qmodelroledataspan-len
+
+// @snippet qmodelroledataspan-getitem
+const Py_ssize_t size = %CPPSELF.size();
+if (_i < 0 || _i >= size) {
+    Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+    return nullptr;
+}
+// Return a pointer to allow for modification using  QModelRoleData::setData()
+QModelRoleData *item = &((*%CPPSELF)[_i]);
+return %CONVERTTOPYTHON[QModelRoleData *](item);
+// @snippet qmodelroledataspan-getitem
+
 // @snippet default-enter
 Py_INCREF(%PYSELF);
 pyResult = %PYSELF;
@@ -630,8 +651,11 @@ if (ret == nullptr) {
 // @snippet qbytearray-mgetitem
 if (PyIndex_Check(_key)) {
     const Py_ssize_t _i = PyNumber_AsSsize_t(_key, PyExc_IndexError);
-    if (_i < 0 || _i >= %CPPSELF.size())
-        return PyErr_Format(PyExc_IndexError, "index out of bounds");
+    const Py_ssize_t size = %CPPSELF.size();
+    if (_i < 0 || _i >= size) {
+        Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+        return nullptr;
+    }
     char res[2] = {%CPPSELF.at(_i), '\0'};
     return PyBytes_FromStringAndSize(res, 1);
 }
@@ -812,7 +836,7 @@ static PyBufferProcs SbkQByteArrayBufferProc = {
 // @snippet qbytearray-bufferprotocol
 
 // @snippet qbytearray-operatorplus-1
-QByteArray ba = QByteArray(PyBytes_AS_STRING(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1)) + *%CPPSELF;
+QByteArray ba = QByteArray(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1)) + *%CPPSELF;
 %PYARG_0 = %CONVERTTOPYTHON[QByteArray](ba);
 // @snippet qbytearray-operatorplus-1
 
@@ -833,7 +857,7 @@ QByteArray ba = *%CPPSELF + QByteArray(PyByteArray_AsString(%PYARG_1), PyByteArr
 // @snippet qbytearray-operatorequalequal
 if (PyUnicode_CheckExact(%PYARG_1)) {
     Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
-    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
     bool cppResult = %CPPSELF == ba;
     %PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
 }
@@ -842,7 +866,7 @@ if (PyUnicode_CheckExact(%PYARG_1)) {
 // @snippet qbytearray-operatornotequal
 if (PyUnicode_CheckExact(%PYARG_1)) {
     Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
-    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
     bool cppResult = %CPPSELF != ba;
     %PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
 }
@@ -851,7 +875,7 @@ if (PyUnicode_CheckExact(%PYARG_1)) {
 // @snippet qbytearray-operatorgreater
 if (PyUnicode_CheckExact(%PYARG_1)) {
     Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
-    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
     bool cppResult = %CPPSELF > ba;
     %PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
 }
@@ -860,7 +884,7 @@ if (PyUnicode_CheckExact(%PYARG_1)) {
 // @snippet qbytearray-operatorgreaterequal
 if (PyUnicode_CheckExact(%PYARG_1)) {
     Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
-    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
     bool cppResult = %CPPSELF >= ba;
     %PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
 }
@@ -869,7 +893,7 @@ if (PyUnicode_CheckExact(%PYARG_1)) {
 // @snippet qbytearray-operatorlower
 if (PyUnicode_CheckExact(%PYARG_1)) {
     Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
-    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
     bool cppResult = %CPPSELF < ba;
     %PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
 }
@@ -878,7 +902,7 @@ if (PyUnicode_CheckExact(%PYARG_1)) {
 // @snippet qbytearray-operatorlowerequal
 if (PyUnicode_CheckExact(%PYARG_1)) {
     Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
-    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+    QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
     bool cppResult = %CPPSELF <= ba;
     %PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
 }
@@ -899,7 +923,7 @@ Py_DECREF(aux);
 // @snippet qbytearray-2
 
 // @snippet qbytearray-3
-%0 = new QByteArray(PyBytes_AS_STRING(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1));
+%0 = new QByteArray(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1));
 // @snippet qbytearray-3
 
 // @snippet qbytearray-py3
@@ -924,15 +948,16 @@ return %CPPSELF.size();
 // @snippet qbytearray-len
 
 // @snippet qbytearray-getitem
-if (_i < 0 || _i >= %CPPSELF.size()) {
-    PyErr_SetString(PyExc_IndexError, "index out of bounds");
-    return 0;
-} else {
-    char res[2];
-    res[0] = %CPPSELF.at(_i);
-    res[1] = 0;
-    return PyBytes_FromStringAndSize(res, 1);
+const Py_ssize_t size = %CPPSELF.size();
+if (_i < 0 || _i >= size) {
+    Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+    return nullptr;
 }
+
+char res[2];
+res[0] = %CPPSELF.at(_i);
+res[1] = 0;
+return PyBytes_FromStringAndSize(res, 1);
 // @snippet qbytearray-getitem
 
 // @snippet qbytearray-setitem
@@ -1061,25 +1086,8 @@ if (msec == 0) {
 
     QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
     timer->setSingleShot(true);
-
-    if (PyObject_TypeCheck(%2, PySideSignalInstance_TypeF())) {
-        PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%2);
-        Shiboken::AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
-        Shiboken::AutoDecRef result(
-            PyObject_CallMethod(pyTimer, "connect", "OsOO",
-                                pyTimer,
-                                SIGNAL(timeout()),
-                                PySide::Signal::getObject(signalInstance),
-                                signalSignature.object())
-        );
-    } else {
-        Shiboken::AutoDecRef result(
-            PyObject_CallMethod(pyTimer, "connect", "OsO",
-                                pyTimer,
-                                SIGNAL(timeout()),
-                                %PYARG_2)
-        );
-    }
+    if (!PySide::callConnect(pyTimer, SIGNAL(timeout()), %PYARG_2))
+        return nullptr;
 
     timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
     Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
@@ -1133,8 +1141,8 @@ if (msec == 0) {
 qint64 pid;
 %RETURN_TYPE retval = %TYPE::%FUNCTION_NAME(%1, %2, %3, &pid);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[qint64](pid));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[qint64](pid));
 // @snippet qprocess-startdetached
 
 // @snippet qcoreapplication-init
@@ -1142,7 +1150,7 @@ static void QCoreApplicationConstructor(PyObject *self, PyObject *pyargv, QCoreA
 {
     static int argc;
     static char **argv;
-    PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+    PyObject *stringlist = PyTuple_GetItem(pyargv, 0);
     if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
         *cptr = new QCoreApplicationWrapper(argc, argv);
         Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(self));
@@ -1230,7 +1238,7 @@ PySideEasingCurveFunctor::init();
 // @snippet qeasingcurve
 
 // @snippet qeasingcurve-setcustomtype
-QEasingCurve::EasingFunction func = PySideEasingCurveFunctor::createCustomFuntion(%PYSELF, %PYARG_1);
+QEasingCurve::EasingFunction func = PySideEasingCurveFunctor::createCustomFunction(%PYSELF, %PYARG_1);
 if (func)
     %CPPSELF.%FUNCTION_NAME(func);
 // @snippet qeasingcurve-setcustomtype
@@ -1265,26 +1273,24 @@ QT_END_NAMESPACE
 // @snippet qt-registerresourcedata
 
 // @snippet qt-qregisterresourcedata
-%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_2)),
-                                     reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_3)),
-                                     reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_4)));
+%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_2)),
+                                     reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_3)),
+                                     reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_4)));
 %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
 // @snippet qt-qregisterresourcedata
 
 // @snippet qt-qunregisterresourcedata
-%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_2)),
-                                     reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_3)),
-                                     reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_4)));
+%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_2)),
+                                     reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_3)),
+                                     reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_4)));
 %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
 // @snippet qt-qunregisterresourcedata
 
-// @snippet use-stream-for-format-security
-// Uses the stream version for security reasons
-// see gcc man page at -Wformat-security
+// @snippet qdebug-format-string
 Py_BEGIN_ALLOW_THREADS
-%FUNCTION_NAME() << %1;
+%FUNCTION_NAME("%s", %1); // Uses placeholder for security reasons
 Py_END_ALLOW_THREADS
-// @snippet use-stream-for-format-security
+// @snippet qdebug-format-string
 
 // @snippet qresource-registerResource
  auto ptr = reinterpret_cast<uchar *>(Shiboken::Buffer::getPointer(%PYARG_1));
@@ -1318,8 +1324,8 @@ QString &res = *%0;
 // @snippet return-readData
 %RETURN_TYPE %0 = 0;
 if (PyBytes_Check(%PYARG_0)) {
-    %0 = PyBytes_GET_SIZE(%PYARG_0.object());
-    memcpy(%1, PyBytes_AS_STRING(%PYARG_0.object()), %0);
+    %0 = PyBytes_Size(%PYARG_0.object());
+    memcpy(%1, PyBytes_AsString(%PYARG_0.object()), %0);
 } else if (Shiboken::String::check(%PYARG_0.object())) {
     %0 = Shiboken::String::len(%PYARG_0.object());
     memcpy(%1, Shiboken::String::toCString(%PYARG_0.object()), %0);
@@ -1351,8 +1357,8 @@ Py_END_ALLOW_THREADS
     if (!atexit.isNull() && !regFunc.isNull()){
         PyObject *shutDownFunc = PyObject_GetAttrString(module, "__moduleShutdown");
         Shiboken::AutoDecRef args(PyTuple_New(1));
-        PyTuple_SET_ITEM(args, 0, shutDownFunc);
-        Shiboken::AutoDecRef retval(PyObject_Call(regFunc, args, 0));
+        PyTuple_SetItem(args, 0, shutDownFunc);
+        Shiboken::AutoDecRef retval(PyObject_Call(regFunc, args, nullptr));
         Q_ASSERT(!retval.isNull());
     }
 }
@@ -1580,7 +1586,7 @@ int usec = PyDateTime_TIME_GET_MICROSECOND(%in);
 // @snippet conversion-qtime-pytime
 
 // @snippet conversion-qbytearray-pybytes
-%out = %OUTTYPE(PyBytes_AS_STRING(%in), PyBytes_GET_SIZE(%in));
+%out = %OUTTYPE(PyBytes_AsString(%in), PyBytes_Size(%in));
 // @snippet conversion-qbytearray-pybytes
 
 // @snippet conversion-qbytearray-pybytearray
@@ -1692,7 +1698,7 @@ if (converterO.has_value())
     return converterO.value().toPython(cppInRef.data());
 
 PyErr_Format(PyExc_RuntimeError, "Can't find converter for '%s'.", %in.typeName());
-return 0;
+return nullptr;
 // @snippet return-qvariant
 
 // @snippet return-qjsonobject
@@ -1803,13 +1809,6 @@ if (dataChar == nullptr) {
 }
 // @snippet qdatastream-read-bytes
 
-// @snippet qloggingcategory_to_cpp
-// PYSIDE-2404: Usage of the `get()` function not necessary, the type exists.
-    QLoggingCategory *category{nullptr};
-    Shiboken::Conversions::pythonToCppPointer(SbkPySide6_QtCoreTypeStructs[SBK_QLoggingCategory_IDX].type,
-    pyArgs[0], &(category));
-// @snippet qloggingcategory_to_cpp
-
 // Q_ARG()-equivalent
 // @snippet q_arg
 const QArgData qArgData = qArgDataFromPyType(%1);
@@ -2071,12 +2070,12 @@ if ((classMethod && (count > 2)) || (!classMethod && (count > 1))) {
 
 bool arg_qpermission = (classMethod && (count == 2)) || (!classMethod && (count == 1));
 
-auto callback = [callable, count, arg_qpermission](const QPermission &permission) -> void
+auto callback = [callable, arg_qpermission](const QPermission &permission) -> void
 {
     Shiboken::GilState state;
     if (arg_qpermission) {
         Shiboken::AutoDecRef arglist(PyTuple_New(1));
-        PyTuple_SET_ITEM(arglist.object(), 0, %CONVERTTOPYTHON[QPermission](permission));
+        PyTuple_SetItem(arglist.object(), 0, %CONVERTTOPYTHON[QPermission](permission));
         Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
     } else {
         Shiboken::AutoDecRef ret(PyObject_CallObject(callable, nullptr));
@@ -2095,9 +2094,9 @@ qint64 pid{};
 QString hostname, appname;
 %CPPSELF.%FUNCTION_NAME(&pid, &hostname, &appname);
 %PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[qint64](pid));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](hostname));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[QString](appname));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[qint64](pid));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](hostname));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[QString](appname));
 // @snippet qlockfile-getlockinfo
 
 // @snippet darwin_permission_plugin
@@ -2148,3 +2147,23 @@ PyRun_String(R"PY(if True:
 
 )PY", Py_file_input, _inputDict, _inputDict);
 // @snippet qt-modifier
+
+// @snippet qdirlisting-iter
+auto result = QtCoreHelper::QDirListingIterator(*%CPPSELF);
+%PYARG_0 = %CONVERTTOPYTHON[QtCoreHelper::QDirListingIterator](result);
+// @snippet qdirlisting-iter
+
+// @snippet qdirlistingiterator-next
+if (%CPPSELF.next()) {
+    Py_INCREF(%PYSELF);
+    %PYARG_0 = %PYSELF;
+}
+// @snippet qdirlistingiterator-next
+
+// @snippet qdirlisting-direntry-repr
+QByteArray result = '<' + QByteArray(Py_TYPE(%PYSELF)->tp_name)
+                    + " object at 0x"
+                    + QByteArray::number(quintptr(%PYSELF), 16) + " (\""
+                    + %CPPSELF.absoluteFilePath().toUtf8() + "\")>";
+%PYARG_0 = Shiboken::String::fromCString(result.constData());
+// @snippet qdirlisting-direntry-repr
index 5c860a2bfc8caf0f6dbd88f269cb24d6f993557e..a91478c55439c75b30f7a177bcc90095be13ac83 100644 (file)
@@ -84,8 +84,8 @@ QAccessibleInterface *PySideAccessibleFactory::callFactories(const QString &key,
 {
     Shiboken::GilState state;
     Shiboken::AutoDecRef arglist(PyTuple_New(2));
-    PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](key));
-    PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QObject *](o));
+    PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QString](key));
+    PyTuple_SetItem(arglist, 1, %CONVERTTOPYTHON[QObject *](o));
 
     for (auto *f : m_factoryFunctions) {
         if (PyObject *pyResult = PyObject_CallObject(f, arglist)) {
@@ -305,14 +305,15 @@ QBitmap %0 = QBitmap::fromData(%1, buffer, %3);
 // @snippet qtextline-cursortox
 %RETURN_TYPE %0 = %CPPSELF->::%TYPE::%FUNCTION_NAME(&%1, %2);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
 // @snippet qtextline-cursortox
 
 // @snippet qkeysequence-getitem
-if (_i < 0 || _i >= %CPPSELF.count()) {
-    PyErr_SetString(PyExc_IndexError, "index out of bounds");
-    return 0;
+const Py_ssize_t size = %CPPSELF.count();
+if (_i < 0 || _i >= size) {
+    Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+    return nullptr;
 }
 QKeyCombination item = (*%CPPSELF)[_i];
 return %CONVERTTOPYTHON[QKeyCombination](item);
@@ -411,8 +412,8 @@ static void imageDecrefDataHandler(void *data)
 // @snippet qimage-scanline
 
 // @snippet qcolor-setstate
-Shiboken::AutoDecRef func(PyObject_GetAttr(%PYSELF, PyTuple_GET_ITEM(%1, 0)));
-PyObject *args = PyTuple_GET_ITEM(%1, 1);
+Shiboken::AutoDecRef func(PyObject_GetAttr(%PYSELF, PyTuple_GetItem(%1, 0)));
+PyObject *args = PyTuple_GetItem(%1, 1);
 %PYARG_0 = PyObject_Call(func, args, nullptr);
 // @snippet qcolor-setstate
 
@@ -687,8 +688,8 @@ for (int r=0, r_max = %CPPSELF.rowCount(); r < r_max; r++) {
 %RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2);
 %END_ALLOW_THREADS
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
 // @snippet qclipboard-text
 
 // @snippet qpainter-drawpointsnp-numpy-x-y
@@ -715,7 +716,7 @@ Shiboken::AutoDecRef seq(PySequence_Fast(%PYARG_1, "Can't turn into sequence"));
 if (PySequence_Size(seq) == 16) {
     float values[16];
     for (Py_ssize_t i = 0; i < 16; ++i) {
-        PyObject *pv = PySequence_Fast_GET_ITEM(seq.object(), i);
+        Shiboken::AutoDecRef pv(PySequence_GetItem(seq.object(), i));
         values[i] = PyFloat_AsDouble(pv);
     }
 
@@ -732,16 +733,16 @@ float values[16];
 %PYARG_0 = PyTuple_New(16);
 for (Py_ssize_t i = 0; i < 16; ++i) {
   PyObject *v = PyFloat_FromDouble(values[i]);
-  PyTuple_SET_ITEM(%PYARG_0, i, v);
+  PyTuple_SetItem(%PYARG_0, i, v);
 }
 // @snippet qmatrix4x4-copydatato
 
 // @snippet qmatrix4x4-mgetitem
 if (PySequence_Check(_key)) {
     Shiboken::AutoDecRef key(PySequence_Fast(_key, "Invalid matrix index."));
-    if (PySequence_Fast_GET_SIZE(key.object()) == 2) {
-        PyObject *posx = PySequence_Fast_GET_ITEM(key.object(), 0);
-        PyObject *posy = PySequence_Fast_GET_ITEM(key.object(), 1);
+    if (PySequence_Size(key.object()) == 2) {
+        Shiboken::AutoDecRef posx(PySequence_GetItem(key.object(), 0));
+        Shiboken::AutoDecRef posy(PySequence_GetItem(key.object(), 1));
         Py_ssize_t x = PyLong_AsSsize_t(posx);
         Py_ssize_t y = PyLong_AsSsize_t(posy);
         float ret = (*%CPPSELF)(x,y);
@@ -757,7 +758,7 @@ static void QGuiApplicationConstructor(PyObject *self, PyObject *pyargv, QGuiApp
 {
     static int argc;
     static char **argv;
-    PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+    PyObject *stringlist = PyTuple_GetItem(pyargv, 0);
     if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
         *cptr = new QGuiApplicationWrapper(argc, argv, 0);
         Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(self));
@@ -894,17 +895,17 @@ QVector3D outVec{};
 float angle{};
 %CPPSELF.%FUNCTION_NAME(&outVec, &angle);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[QVector3D](outVec));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[float](angle));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QVector3D](outVec));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[float](angle));
 // @snippet qquaternion-getaxisandangle-vector3d-float
 
 // @snippet qquaternion-geteulerangles
 float pitch{}, yaw{}, roll{};
 %CPPSELF.%FUNCTION_NAME(&pitch, &yaw, &roll);
 %PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[float](pitch));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[float](yaw));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[float](roll));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[float](pitch));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[float](yaw));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[float](roll));
 // @snippet qquaternion-geteulerangles
 
 // @snippet qregion-len
@@ -912,8 +913,11 @@ return %CPPSELF.rectCount();
 // @snippet qregion-len
 
 // @snippet qregion-getitem
-if (_i < 0 || _i >= %CPPSELF.rectCount())
-    return PyErr_Format(PyExc_IndexError, "index out of bounds");
+const Py_ssize_t size = %CPPSELF.rectCount();
+if (_i < 0 || _i >= size) {
+    Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+    return nullptr;
+}
 
 const QRect cppResult = *(%CPPSELF.cbegin() + _i);
 return %CONVERTTOPYTHON[QRect](cppResult);
index f635f4671cf2995254bb84d7c476357b7674fc01..7b1c2d5646de3ac9eac5bc1750200b2080e0a54e 100644 (file)
@@ -10,9 +10,9 @@ quint16 port;
 %END_ALLOW_THREADS
 QByteArray ba(data, retval);
 %PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[QByteArray](ba));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QHostAddress](ha));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[quint16](port));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QByteArray](ba));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QHostAddress](ha));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[quint16](port));
 // @snippet qudpsocket-readdatagram
 
 // @snippet qhostinfo-lookuphost-functor
@@ -29,7 +29,7 @@ void QHostInfoFunctor::operator()(const QHostInfo &hostInfo)
     Shiboken::GilState state;
     Shiboken::AutoDecRef arglist(PyTuple_New(1));
     auto *pyHostInfo = %CONVERTTOPYTHON[QHostInfo](hostInfo);
-    PyTuple_SET_ITEM(arglist.object(), 0, pyHostInfo);
+    PyTuple_SetItem(arglist.object(), 0, pyHostInfo);
     Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
     release(); // single shot
 }
@@ -46,7 +46,7 @@ return 16;
 // @snippet qipv6address-getitem
 if (_i >= 16) {
     PyErr_SetString(PyExc_IndexError, "index out of bounds");
-    return 0;
+    return nullptr;
 }
 if (_i < 0)
     _i = 16 - qAbs(_i);
@@ -89,7 +89,7 @@ void QRestFunctor::operator()(QRestReply &restReply)
     Shiboken::AutoDecRef arglist(PyTuple_New(1));
     auto *restReplyPtr = &restReply;
     auto *pyRestReply = %CONVERTTOPYTHON[QRestReply*](restReplyPtr);
-    PyTuple_SET_ITEM(arglist.object(), 0, pyRestReply);
+    PyTuple_SetItem(arglist.object(), 0, pyRestReply);
     Shiboken::AutoDecRef ret(PyObject_CallObject(m_callable, arglist));
     Py_DECREF(m_callable);
     m_callable = nullptr;
index 7877a8dd5cc4f15b25744a9fd9623b89231bcc7a..aa28f500c81cd35de0602249ce88a71e0cdd70ad 100644 (file)
@@ -21,8 +21,8 @@ void QAbstractOAuthModifyFunctor::operator()(QAbstractOAuth::Stage stage,
     Shiboken::GilState state;
     QMultiMap<QString, QVariant> dict = *dictPointer;
     Shiboken::AutoDecRef arglist(PyTuple_New(2));
-    PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QAbstractOAuth::Stage](stage));
-    PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QMultiMap<QString, QVariant>](dict));
+    PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QAbstractOAuth::Stage](stage));
+    PyTuple_SetItem(arglist, 1, %CONVERTTOPYTHON[QMultiMap<QString, QVariant>](dict));
     Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
 
     if (!ret.isNull() && PyDict_Check(ret.object()) != 0) {
@@ -41,4 +41,3 @@ void QAbstractOAuthModifyFunctor::operator()(QAbstractOAuth::Stage stage,
 // @snippet qabstractoauth-setmodifyparametersfunction
 %CPPSELF.%FUNCTION_NAME(QAbstractOAuthModifyFunctor(%PYARG_1));
 // @snippet qabstractoauth-setmodifyparametersfunction
-
diff --git a/sources/pyside6/PySide6/glue/qtquick3d.cpp b/sources/pyside6/PySide6/glue/qtquick3d.cpp
new file mode 100644 (file)
index 0000000..eee2864
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*********************************************************************
+ * INJECT CODE
+ ********************************************************************/
+
+// @snippet qquick3dinstancing-getinstancebuffer-virtual-redirect
+std::pair<QByteArray, int> resultPair = getInstanceBufferOverride(gil, pyOverride.object());
+if (instanceCount != nullptr)
+    *instanceCount = resultPair.second;
+return resultPair.first;
+// @snippet qquick3dinstancing-getinstancebuffer-virtual-redirect
+
+// @snippet qquick3dinstancing-getinstancebuffer-return
+int count{};
+%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(&count);
+%PYARG_0 = PyTuple_New(2);
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](count));
+// @snippet qquick3dinstancing-getinstancebuffer-return
index b5b491f49de589f155b45a7f745274c0747c90f9..0bf918fde23fcf553b02ecf8e57fe3e9497b6b5f 100644 (file)
@@ -7,12 +7,20 @@
 
 // @snippet qcanbus-available-devices
 QString errorMessage;
-const QList<QCanBusDeviceInfo> result = %CPPSELF.%FUNCTION_NAME(%1, &errorMessage);
+const QList<QCanBusDeviceInfo> result = %CPPSELF.%FUNCTION_NAME(&errorMessage);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[QList<QCanBusDeviceInfo>](result));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QList<QCanBusDeviceInfo>](result));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
 // @snippet qcanbus-available-devices
 
+// @snippet qcanbus-available-devices-plugin
+QString errorMessage;
+const QList<QCanBusDeviceInfo> result = %CPPSELF.%FUNCTION_NAME(%1, &errorMessage);
+%PYARG_0 = PyTuple_New(2);
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QList<QCanBusDeviceInfo>](result));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
+// @snippet qcanbus-available-devices-plugin
+
 // @snippet qcanbus-createdevice
 PyObject *pyDevice{};
 QString errorMessage;
@@ -25,6 +33,6 @@ if (auto *device = %CPPSELF.%FUNCTION_NAME(%1, %2, &errorMessage)) {
     Py_INCREF(pyDevice);
 }
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, pyDevice);
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
+PyTuple_SetItem(%PYARG_0, 0, pyDevice);
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
 // @snippet qcanbus-createdevice
index 098200b14caf079322761a53704f3efc211d2d7d..66c4acf10a79f6cea8a9ecd452e02e23cee01ef2 100644 (file)
@@ -41,7 +41,8 @@ if (PySide::SignalManager::registerMetaMethod(%1, signalName.constData(),
 // http://bugs.openbossa.org/show_bug.cgi?id=362
 // PYSIDE-2256: The label was removed
 if (!PyObject_TypeCheck(%1, PySideSignalInstance_TypeF()))
-    return Shiboken::returnWrongArguments(args, fullName, errInfo);
+    return Shiboken::returnWrongArguments(args, "addTransition", errInfo,
+        SbkPySide6_QtStateMachineTypeStructs[SBK_QAbstractTransition_IDX]);
 PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%1);
 auto sender = %CONVERTTOCPP[QObject *](PySide::Signal::getObject(signalInstance));
 QSignalTransition *%0 = %CPPSELF->%FUNCTION_NAME(sender, PySide::Signal::getSignature(signalInstance),%2);
index 626cc1608bf1c4a099a2ddfd3d6c3ff474d09615..36d3bfa37b0fe09ba2482b0a435a496804892454 100644 (file)
@@ -20,5 +20,5 @@ if (emitter == nullptr || signature.isEmpty()) {
     PyErr_SetString(PyExc_ValueError, error.constData());
     return -1;
 }
-%0 = new QSignalSpyWrapper(emitter, signature.constData());
+%0 = new QSignalSpy(emitter, signature.constData());
 // @snippet qsignalspy-signal
index 1835ed096856fe55e42a19f3900619e2aa946659..3fe3554c4f8eb73a10ba393dc6413ab1cd8432ee 100644 (file)
@@ -207,6 +207,6 @@ if (%PYARG_0 == nullptr) {
     qCritical() << "Error while creating the return Tuple";
     Py_RETURN_NONE;
 }
-PyTuple_SET_ITEM(%PYARG_0, 0, classObj);
-PyTuple_SET_ITEM(%PYARG_0, 1, baseClassObj);
+PyTuple_SetItem(%PYARG_0, 0, classObj);
+PyTuple_SetItem(%PYARG_0, 1, baseClassObj);
 // @snippet loaduitype
index 76a7c6d73359802ba47878996cce5c29a0b2662f..42ac6e7687761ebbbe92c4c2f7ac6f489c8d4eeb 100644 (file)
@@ -14,7 +14,7 @@ bool QWebEngineCookieFilterFunctor::operator()(const QWebEngineCookieStore::Filt
 {
     Shiboken::GilState state;
     Shiboken::AutoDecRef arglist(PyTuple_New(1));
-    PyTuple_SET_ITEM(arglist, 0,
+    PyTuple_SetItem(arglist, 0,
                      %CONVERTTOPYTHON[QWebEngineCookieStore::FilterRequest](filterRequest));
     Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
     return ret.object() == Py_True;
@@ -39,7 +39,7 @@ void QWebEngineNotificationFunctor::operator()
     Shiboken::GilState state;
     Shiboken::AutoDecRef arglist(PyTuple_New(1));
     auto *notification = webEngineNotification.release();
-    PyTuple_SET_ITEM(arglist.object(), 0,
+    PyTuple_SetItem(arglist.object(), 0,
                      %CONVERTTOPYTHON[QWebEngineNotification*](notification));
     Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
 };
@@ -59,6 +59,101 @@ return resultPair.first;
 QString str;
 %RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, &str);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](str));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](str));
 // @snippet qwebenginepage-javascriptprompt-return
+
+// @snippet qwebenginepage-findtext
+auto callable = %PYARG_3;
+auto callback = [callable](const QWebEngineFindTextResult &result)
+{
+    Shiboken::GilState state;
+    Shiboken::AutoDecRef arglist(PyTuple_New(1));
+    PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QWebEngineFindTextResult](result));
+    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
+    Py_DECREF(callable);
+
+};
+Py_INCREF(callable);
+%CPPSELF.%FUNCTION_NAME(%1, %2, callback);
+// @snippet qwebenginepage-findtext
+
+// @snippet qwebenginepage-print
+auto printer = %PYARG_1;
+auto callable = %PYARG_2;
+auto callback = [printer, callable](bool succeeded)
+{
+    if (!PyCallable_Check(callable)) {
+        qWarning("Argument 2 of %FUNCTION_NAME must be a callable.");
+        return;
+    }
+    Shiboken::GilState state;
+    Shiboken::AutoDecRef arglist(PyTuple_New(1));
+    PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[bool](succeeded));
+    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
+    Py_DECREF(callable);
+    Py_DECREF(printer);
+
+};
+Py_INCREF(printer); // Add a reference to the printer until asynchronous printing has finished
+Py_INCREF(callable);
+%CPPSELF.%FUNCTION_NAME(%1, callback);
+// @snippet qwebenginepage-print
+
+// @snippet qwebenginepage-convertto
+auto callable = %PYARG_1;
+auto callback = [callable](const QString &text)
+{
+    Shiboken::GilState state;
+    Shiboken::AutoDecRef arglist(PyTuple_New(1));
+    PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QString](text));
+    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
+    Py_DECREF(callable);
+};
+
+Py_INCREF(callable);
+%CPPSELF.%FUNCTION_NAME(callback);
+// @snippet qwebenginepage-convertto
+
+// @snippet qwebenginepage-runjavascript-2
+using RunJavascriptCallback = std::function<void(const QVariant &)>;
+
+if (%PYARG_2 != nullptr && %PYARG_2 != Py_None) {
+    %CPPSELF.%FUNCTION_NAME(%1, RunJavascriptCallback(RunJavascriptFunctor(%PYARG_2)));
+} else {
+    %CPPSELF.%FUNCTION_NAME(%1, 0, RunJavascriptCallback{});
+}
+// @snippet qwebenginepage-runjavascript-2
+
+// @snippet qwebenginepage-runjavascript-3
+using RunJavascriptCallback = std::function<void(const QVariant &)>;
+
+if (%PYARG_3 != nullptr && %PYARG_3 != Py_None) {
+    %CPPSELF.%FUNCTION_NAME(%1, %2, RunJavascriptCallback(RunJavascriptFunctor(%PYARG_3)));
+} else {
+    %CPPSELF.%FUNCTION_NAME(%1, %2, RunJavascriptCallback{});
+}
+// @snippet qwebenginepage-runjavascript-3
+
+// @snippet qwebenginepage-printtopdf
+using PrintToPdfCallback = std::function<void(const QByteArray &)>;
+
+%CPPSELF.%FUNCTION_NAME(PrintToPdfCallback(PrintToPdfFunctor(%PYARG_1)), %2, %3);
+// @snippet qwebenginepage-printtopdf
+
+// @snippet qwebenginepage-findframebyname
+auto frameOptional = %CPPSELF.%FUNCTION_NAME(%1);
+if (frameOptional.has_value()) {
+    const %RETURN_TYPE &frame = frameOptional.value();
+    %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](frame);
+} else {
+    Py_INCREF(Py_None);
+    %PYARG_0 = Py_None;
+}
+// @snippet qwebenginepage-findframebyname
+
+// @snippet qwebengineframe-printtopdf
+using PrintToPdfCallback = std::function<void(const QByteArray &)>;
+
+%CPPSELF.%FUNCTION_NAME(PrintToPdfCallback(PrintToPdfFunctor(%PYARG_1)));
+// @snippet qwebengineframe-printtopdf
diff --git a/sources/pyside6/PySide6/glue/qtwebenginewidgets.cpp b/sources/pyside6/PySide6/glue/qtwebenginewidgets.cpp
deleted file mode 100644 (file)
index af15130..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-// @snippet qwebenginepage-findtext
-auto callable = %PYARG_3;
-auto callback = [callable](const QWebEngineFindTextResult &result)
-{
-    if (!PyCallable_Check(callable)) {
-        qWarning("Argument 3 of %FUNCTION_NAME must be a callable.");
-        return;
-    }
-    Shiboken::GilState state;
-    Shiboken::AutoDecRef arglist(PyTuple_New(1));
-    PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QWebEngineFindTextResult](result));
-    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
-    Py_DECREF(callable);
-
-};
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(%1, %2, callback);
-// @snippet qwebenginepage-findtext
-
-// @snippet qwebenginepage-print
-auto printer = %PYARG_1;
-auto callable = %PYARG_2;
-auto callback = [printer, callable](bool succeeded)
-{
-    if (!PyCallable_Check(callable)) {
-        qWarning("Argument 2 of %FUNCTION_NAME must be a callable.");
-        return;
-    }
-    Shiboken::GilState state;
-    Shiboken::AutoDecRef arglist(PyTuple_New(1));
-    PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[bool](succeeded));
-    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
-    Py_DECREF(callable);
-    Py_DECREF(printer);
-
-};
-Py_INCREF(printer); // Add a reference to the printer until asynchronous printing has finished
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(%1, callback);
-// @snippet qwebenginepage-print
-
-// @snippet qwebenginepage-convertto
-auto callable = %PYARG_1;
-auto callback = [callable](const QString &text)
-{
-    if (!PyCallable_Check(callable)) {
-        qWarning("Argument 1 of %FUNCTION_NAME must be a callable.");
-        return;
-    }
-    Shiboken::GilState state;
-    Shiboken::AutoDecRef arglist(PyTuple_New(1));
-    PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](text));
-    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
-    Py_DECREF(callable);
-};
-
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(callback);
-// @snippet qwebenginepage-convertto
-
-// @snippet qwebenginepage-runjavascript
-auto callable = %PYARG_3;
-auto callback = [callable](const QVariant &result)
-{
-    if (!PyCallable_Check(callable)) {
-        qWarning("Argument 3 of %FUNCTION_NAME must be a callable.");
-        return;
-    }
-    Shiboken::GilState state;
-    Shiboken::AutoDecRef arglist(PyTuple_New(1));
-    switch (result.type()) {
-    case QVariant::Bool: {
-        const bool value = result.toBool();
-        PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](value));
-    }
-        break;
-     case QVariant::Int:
-     case QVariant::UInt:
-     case QVariant::LongLong:
-     case QVariant::ULongLong:
-     case QVariant::Double: {
-        const double number = result.toDouble();
-        PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[double](number));
-    }
-        break;
-    default: {
-        const QString value = result.toString();
-        PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](value));
-    }
-        break;
-    }
-   // PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[bool](found));
-    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
-    Py_DECREF(callable);
-};
-
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(%1, %2, callback);
-// @snippet qwebenginepage-runjavascript
-
-// @snippet qwebenginepage-printtopdf
-auto callable = %PYARG_1;
-auto callback = [callable](const QByteArray &pdf)
-{
-    if (!PyCallable_Check(callable)) {
-        qWarning("Argument 1 of %FUNCTION_NAME must be a callable.");
-        return;
-    }
-    Shiboken::GilState state;
-    Shiboken::AutoDecRef arglist(PyTuple_New(1));
-    PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QByteArray](pdf));
-    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
-    Py_DECREF(callable);
-};
-
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(callback, %2);
-// @snippet qwebenginepage-printtopdf
index f886106cf4bfc57c8911b5f4adfc094c158663f1..bc9dc99d62cb5614bc917b09debb25d9d5d4c6cb 100644 (file)
@@ -36,8 +36,8 @@ if (%0) {
 QGraphicsItem *item_ = nullptr;
 %RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(&item_);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QGraphicsItem *](item_));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QGraphicsItem *](item_));
 // @snippet qgraphicsitem-isblockedbymodalpanel
 
 // @snippet qitemeditorfactory-registereditor
@@ -60,13 +60,13 @@ int _row;
 QFormLayout::ItemRole _role;
 %CPPSELF->%FUNCTION_NAME(%ARGUMENT_NAMES, &_row, &_role);
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](_row));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](_row));
 // On the C++ side, *rolePtr is not set if row == -1, in which case on
 // the Python side this gets converted to a random value outside the
 // enum range. Fix this by setting _role to a default value here.
 if (_row == -1)
     _role = QFormLayout::LabelRole;
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QFormLayout::ItemRole](_role));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QFormLayout::ItemRole](_role));
 // @snippet qformlayout-fix-args
 
 // @snippet qfiledialog-return
@@ -74,8 +74,8 @@ PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QFormLayout::ItemRole](_role));
 %RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, %4, &%5, %6);
 %END_ALLOW_THREADS
 %PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](%5));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](%5));
 // @snippet qfiledialog-return
 
 // @snippet qwidget-addaction-glue
@@ -392,10 +392,10 @@ removeLayoutOwnership(%CPPSELF, %1);
 int a, b, c, d;
 %CPPSELF.%FUNCTION_NAME(%1, &a, &b, &c, &d);
 %PYARG_0 = PyTuple_New(4);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](a));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](b));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](c));
-PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[int](d));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](a));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](b));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](c));
+PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[int](d));
 // @snippet qgridlayout-getitemposition
 
 // @snippet qgraphicsscene-destroyitemgroup
@@ -569,7 +569,7 @@ static void QApplicationConstructor(PyObject *self, PyObject *pyargv, QApplicati
 {
     static int argc;
     static char **argv;
-    PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+    PyObject *stringlist = PyTuple_GetItem(pyargv, 0);
     if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
         *cptr = new QApplicationWrapper(argc, argv, 0);
         Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(self));
@@ -809,6 +809,12 @@ const QByteArray signature = QByteArrayLiteral("2") + method.methodSignature();
 Shiboken::Object::removeParent(reinterpret_cast<SbkObject *>(%PYSELF));
 // @snippet qdialog-exec-remove-parent-relation
 
+// @snippet qmessagebox-open-connect-accept
+if (!PySide::callConnect(%PYSELF, SIGNAL(accepted()), %PYARG_1))
+    return nullptr;
+%CPPSELF.%FUNCTION_NAME();
+// @snippet qmessagebox-open-connect-accept
+
 /*********************************************************************
  * CONVERSIONS
  ********************************************************************/
index 120579927e9d6d4db6f27f4842ddf3f66d38f02b..b97d774e7f274c3289b10407e65feb236aedbc5b 100644 (file)
@@ -10,8 +10,8 @@ bool _ret_ = %CPPSELF.%FUNCTION_NAME(%ARGUMENT_NAMES, &_errorMsg_, &_errorLine_,
     &_errorColumn_);
 %END_ALLOW_THREADS
 %PYARG_0 = PyTuple_New(4);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[bool](_ret_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](_errorMsg_));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](_errorLine_));
-PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[int](_errorColumn_));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[bool](_ret_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](_errorMsg_));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](_errorLine_));
+PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[int](_errorColumn_));
 // @snippet qdomdocument-setcontent
index 45bb0bd5281bd9665a5dd5b3663406e48dd90afc..730cc441bf31c24bf84348557bc7b582d0672cc5 100644 (file)
@@ -24,5 +24,3 @@ public:
 QT_END_NAMESPACE
 
 #endif
-
-
index 589d0b295176579df8300fb7f5eb985a9571e23d..6bce2f5f41b585670f6d785837eae7bf82e79475 100644 (file)
@@ -4,6 +4,7 @@
 #ifndef QTCOREHELPER_H
 #define QTCOREHELPER_H
 
+#include <QtCore/qdirlisting.h>
 #include <QtCore/qmutex.h>
 #include <QtCore/qobjectdefs.h>
 
@@ -115,6 +116,28 @@ namespace QtCoreHelper {
         std::shared_ptr<QGenericReturnArgumentData> d;
     };
 
+    struct QDirListingIteratorPrivate;
+
+    class QDirListingIterator
+    {
+    public:
+        explicit QDirListingIterator(const QDirListing &dl);
+        QDirListingIterator();
+
+        QDirListingIterator(const QDirListingIterator &);
+        QDirListingIterator &operator=(const QDirListingIterator &);
+        QDirListingIterator(QDirListingIterator &&) noexcept;
+        QDirListingIterator &operator=(QDirListingIterator &&) noexcept;
+        ~QDirListingIterator();
+
+        bool next();
+        const QDirListing::DirEntry &value() const;
+        bool atEnd() const;
+
+    private:
+        std::shared_ptr<QDirListingIteratorPrivate> d;
+    };
+
 } // namespace QtCoreHelper
 
 QT_END_NAMESPACE
index 5127df0f458d946fe69fb193aec09ecbddabbdb7..e64ee57c0603dce496c307a49bb23cbee34b2c89 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from shiboken6.Shiboken import VoidPtr
 
index 263dd3ed7fa60108923872ebefa0f222dc93aff1..bacafce70de91f16639023b9c83780e376aa377e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 deprecated.py
index 92808e2a533e3992c46b612cd0ca26aad12c959e..7eee7705d7107bdfca41498614dd98816dbd8fae 100644 (file)
@@ -1,11 +1,13 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 generate_pyi.py
 
 This script generates the .pyi files for all PySide modules.
 """
+# mypy: disable-error-code="import-not-found"
 
 import argparse
 import inspect  # noqa: F401
index 44ed931e7e62b88b7e207ecf4c8b67e7d85ed35f..aece7eaa9c1b68d460773aea4aac90e1c9cf894a 100644 (file)
@@ -18,7 +18,7 @@
     </template>
 
     <template name="pybytes_const_uchar">
-        const uchar *%out = reinterpret_cast&lt;const uchar*>(PyBytes_AS_STRING(%PYARG_1));
+        const uchar *%out = reinterpret_cast&lt;const uchar*>(PyBytes_AsString(%PYARG_1));
     </template>
 
     <template name="pybytes_uint">
index 07bafc6ff5b3684840e67f1a15423f666c714d71..c794e7b8f780e94674762a0db183f97760a4c98a 100644 (file)
@@ -7,8 +7,8 @@
 
     <template name="tuple_ok_retval">
         %PYARG_0 = PyTuple_New(2);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[bool](ok_));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[bool](ok_));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
     </template>
 
     <template name="bool*_fix,arg">
@@ -26,8 +26,8 @@
     <!-- Templates to fix bool* parameters -->
     <template name="tuple_retval_ok">
         %PYARG_0 = PyTuple_New(2);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[bool](ok_));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[bool](ok_));
     </template>
 
     <template name="fix_bool*">
         char val_{};
         %RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(&amp;val_);
         %PYARG_0 = PyTuple_New(2);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[char](val_));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[char](val_));
     </template>
 
     <template name="tuple_abcd_same_type">
         %PYARG_0 = PyTuple_New(4);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
-        PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
-        PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
+        PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
+        PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
     </template>
 
     <template name="fix_number*,number*,number*,number*">
 
     <template name="fix_native_return_number*,number*,number*,number*">
         PyObject* _obj = %PYARG_0.object();
+        Shiboken::AutoDecRef _obj0(PySequence_GetItem(_obj, 0));
+        Shiboken::AutoDecRef _obj1(PySequence_GetItem(_obj, 1));
+        Shiboken::AutoDecRef _obj2(PySequence_GetItem(_obj, 2));
+        Shiboken::AutoDecRef _obj3(PySequence_GetItem(_obj, 3));
         if (!PySequence_Check(_obj)
-            || PySequence_Fast_GET_SIZE(_obj) != 4
-            || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 0))
-            || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 1))
-            || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 2))
-            || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 3))) {
+            || PySequence_Size(_obj) != 4
+            || !PyNumber_Check(_obj0)
+            || !PyNumber_Check(_obj1)
+            || !PyNumber_Check(_obj2)
+            || !PyNumber_Check(_obj3)) {
             PyErr_SetString(PyExc_TypeError, "Sequence of 4 numbers expected");
         } else {
-            *%1 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 0));
-            *%2 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 1));
-            *%3 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 2));
-            *%4 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 3));
+            *%1 = %CONVERTTOCPP[$TYPE](_obj0);
+            *%2 = %CONVERTTOCPP[$TYPE](_obj1);
+            *%3 = %CONVERTTOCPP[$TYPE](_obj2);
+            *%4 = %CONVERTTOCPP[$TYPE](_obj3);
         }
     </template>
 
         $TYPE a, b, c, d, e;
         %CPPSELF.%FUNCTION_NAME(&amp;a, &amp;b, &amp;c, &amp;d, &amp;e);
         %PYARG_0 = PyTuple_New(5);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
-        PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
-        PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
-        PyTuple_SET_ITEM(%PYARG_0, 4, %CONVERTTOPYTHON[$TYPE](e));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
+        PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
+        PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
+        PyTuple_SetItem(%PYARG_0, 4, %CONVERTTOPYTHON[$TYPE](e));
     </template>
 
     <template name="fix_args,number*,number*">
         $TYPE a, b;
         %CPPSELF.%FUNCTION_NAME(%ARGUMENT_NAMES, &amp;a, &amp;b);
         %PYARG_0 = PyTuple_New(2);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
     </template>
 
     <template name="fix_arg,int*,int*">
     int a, b;
     _ret = %CPPSELF.%FUNCTION_NAME(%1, &amp;a, &amp;b);
     %PYARG_0 = PyTuple_New(3);
-    PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](_ret));
-    PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](a));
-    PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](b));
+    PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](_ret));
+    PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](a));
+    PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](b));
     </template>
 
     <template name="return_tuple_QValidator_QString_int">
         %RETURN_TYPE retval_ = %RETURN_TYPE(%CPPSELF.%FUNCTION_NAME(%1, %2));
         %PYARG_0 = PyTuple_New(3);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
-        PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+        PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
     </template>
 
     <template name="repr_code">
 
     <template name="return_native_eventfilter">
         %PYARG_0 = PyTuple_New(2);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[long](*result_out));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[long](*result_out));
     </template>
 
     <!-- templates for __reduce__ -->
index 6a7404507ff2b9fdfcfb4003aed8290a538decd6..e21d356d7510f33e2af99a3a09438717027f3940 100644 (file)
 
     <template name="load_xpm">
         Shiboken::AutoDecRef strList(PySequence_Fast(%PYARG_1, "Invalid sequence."));
-        Py_ssize_t lineCount = PySequence_Fast_GET_SIZE(strList.object());
+        Py_ssize_t lineCount = PySequence_Size(strList.object());
         for (Py_ssize_t line = 0; line &lt; lineCount; ++line) {
-            if (!Shiboken::String::check(PySequence_Fast_GET_ITEM(strList.object(), line))) {
+            Shiboken::AutoDecRef _obj(PySequence_GetItem(strList.object(), line));
+            bool isString = Shiboken::String::check(_obj);
+            if (!isString) {
                 PyErr_SetString(PyExc_TypeError, "The argument must be a sequence of strings.");
                 break;
             }
             break;
 
         auto xpm = new const char*[lineCount];
-        for (Py_ssize_t line = 0; line &lt; lineCount; ++line)
-            xpm[line] = Shiboken::String::toCString(PySequence_Fast_GET_ITEM(strList.object(), line));
+        for (Py_ssize_t line = 0; line &lt; lineCount; ++line) {
+            Shiboken::AutoDecRef _obj(PySequence_GetItem(strList.object(), line));
+            xpm[line] = Shiboken::String::toCString(_obj);
+        }
 
         %0 = new %TYPE(xpm);
 
@@ -42,8 +46,8 @@
         %ARG1_TYPE a, b;
         %CPPSELF.%FUNCTION_NAME(%1, %2, &amp;a, &amp;b);
         %PYARG_0 = PyTuple_New(2);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](a));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](b));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](a));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](b));
     </template>
 
     <template name="qimage_buffer_constructor">
 
           if (PySequence_Check(%PYARG_0)) {
               Shiboken::AutoDecRef seq(PySequence_Fast(%PYARG_0, 0));
-              const Py_ssize_t size = PySequence_Fast_GET_SIZE(seq.object());
+              const Py_ssize_t size = PySequence_Size(seq.object());
 
               if (size > 1) {
-                  if (%ISCONVERTIBLE[QString](PySequence_Fast_GET_ITEM(seq.object(), 1)))
-                      %1 = %CONVERTTOCPP[QString](PySequence_Fast_GET_ITEM(seq.object(), 1));
+                  Shiboken::AutoDecRef _obj1(PySequence_GetItem(seq.object(), 1));
+                  if (%ISCONVERTIBLE[QString](_obj1))
+                      %1 = %CONVERTTOCPP[QString](_obj1);
                   else
                       qWarning("%TYPE::%FUNCTION_NAME: Second tuple element is not convertible to unicode.");
               }
 
               if (size > 2) {
-                  if (%ISCONVERTIBLE[int](PySequence_Fast_GET_ITEM(seq.object(), 2)))
-                      %2 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(seq.object(), 2));
+                  Shiboken::AutoDecRef _obj2(PySequence_GetItem(seq.object(), 2));
+                  if (%ISCONVERTIBLE[int](_obj2))
+                      %2 = %CONVERTTOCPP[int](_obj2);
                   else
                       qWarning("%TYPE::%FUNCTION_NAME: Second tuple element is not convertible to int.");
               }
-              %PYARG_0.reset(PySequence_Fast_GET_ITEM(seq.object(), 0));
+              Shiboken::AutoDecRef _sobj(PySequence_GetItem(seq.object(), 0));
+
+              %PYARG_0.reset(_sobj);
               Py_INCREF(%PYARG_0); // we need to incref, because "%PYARG_0 = ..." will decref the tuple and the tuple will be decrefed again at the end of this scope.
           }
 
         PyObject *pyData = PyTuple_New(%MATRIX_SIZE);
         if (data) {
             for(int i=0; i &lt; %MATRIX_SIZE; i++)
-                PyTuple_SET_ITEM(pyData, i, %CONVERTTOPYTHON[float](data[i]));
+                PyTuple_SetItem(pyData, i, %CONVERTTOPYTHON[float](data[i]));
         }
         return pyData;
     </template>
                 "Failed to parse sequence on %TYPE constructor."));
             float values[%SIZE];
             for(int i=0; i &lt; %SIZE; i++) {
-                PyObject *pv = PySequence_Fast_GET_ITEM(fast.object(), i);
+                Shiboken::AutoDecRef pv(PySequence_GetItem(fast.object(), i));
                 values[i] = %CONVERTTOCPP[float](pv);
             }
             %0 = new %TYPE(values);
     </template>
 
     <template name="pybytes_const_uchar">
-        const uchar *%out = reinterpret_cast&lt;const uchar*>(PyBytes_AS_STRING(%PYARG_1));
+        const uchar *%out = reinterpret_cast&lt;const uchar*>(PyBytes_AsString(%PYARG_1));
     </template>
 
     <template name="pybytes_uint">
index 566ebe6ee1cb3a97632fa12a6ad698409405edbd..3435ef810a77995b663b14ce5888ab738a745666 100644 (file)
@@ -12,7 +12,7 @@
               Shiboken::AutoDecRef fast(PySequence_Fast(%PYARG_2,
                 "Failed to parse sequence with type %VECTOR_TYPE."));
               for(Py_ssize_t i=0; i &lt; _size; i++) {
-                  PyObject* pv = PySequence_Fast_GET_ITEM(fast.object(), i);
+                  Shiboken::AutoDecRef pv(PySequence_GetItem(fast.object(), i));
                   _list[i] = %CONVERTTOCPP[$ATTR_TYPE](pv);
               }
             }
index b0dee43cd499913ba92ff98ae7bcfbf9d4272924..39dd80cf8f8e57c9cafc543c2b847100d91463bd 100644 (file)
@@ -19,7 +19,8 @@
         const Py_ssize_t numItems = PySequence_Size(%PYARG_1);
         Shiboken::AutoArrayPointer&lt;QGraphicsItem*&gt; %out(numItems);
         for (Py_ssize_t i = 0; i &lt; numItems; ++i) {
-            %out[i] = %CONVERTTOCPP[QGraphicsItem*](PySequence_Fast_GET_ITEM(%PYARG_1, i));
+            Shiboken::AutoDecRef _arg1(PySequence_GetItem(%PYARG_1, i));
+            %out[i] = %CONVERTTOCPP[QGraphicsItem*](_arg1);
         }
     </template>
 
         const Py_ssize_t numOptions = PySequence_Size(%PYARG_2);
         Shiboken::AutoArrayPointer&lt;QStyleOptionGraphicsItem&gt; %out(numOptions);
         for (Py_ssize_t i=0; i &lt; numOptions; ++i) {
-            %out[i] = %CONVERTTOCPP[QStyleOptionGraphicsItem](PySequence_Fast_GET_ITEM(%PYARG_1, i));
+            Shiboken::AutoDecRef _arg1(PySequence_GetItem(%PYARG_1, i));
+            %out[i] = %CONVERTTOCPP[QStyleOptionGraphicsItem](_arg1);
         }
     </template>
 
     <template name="pysequencesize_int">
-        int %out = PySequence_Size(%PYARG_1);
+        Py_ssize_t %out = PySequence_Size(%PYARG_1);
     </template>
 
 </typesystem>
index 1e434f9f68377b3a21eae2f051b64149b8831b12..a83f2b7453963fcc43edca019300ce02e9159cc2 100644 (file)
@@ -127,7 +127,9 @@ macro(create_pyside_module)
         list(APPEND shiboken_include_dir_list ${${module_ADDITIONAL_INCLUDE_DIRS}})
     endif()
 
-    # Transform the path separators into something shiboken understands.
+    # Remove duplicates and transform the path separators into something
+    # shiboken understands.
+    list(REMOVE_DUPLICATES shiboken_include_dir_list)
     make_path(shiboken_include_dirs ${shiboken_include_dir_list})
 
     set(force_process_system_include_paths_list "")
@@ -174,6 +176,14 @@ macro(create_pyside_module)
         set(${module_NAME}_glue_dependency ${${module_NAME}_glue})
     endif()
 
+    # Install module doc files.
+    set(${module_NAME}_doc "${CMAKE_CURRENT_SOURCE_DIR}/../doc/${lower_module_name}.rst")
+    set(${module_name}_doc_dependency "")
+    if(EXISTS ${${module_NAME}_doc})
+        install(FILES ${${module_NAME}_doc} DESTINATION share/PySide6${pyside6_SUFFIX}/doc)
+        set(${module_NAME}_doc_dependency ${${module_NAME}_doc})
+    endif()
+
     # Install standalone glue files into typesystems subfolder, so that the resolved relative
     # paths remain correct.
     if (module_GLUE_SOURCES)
@@ -194,6 +204,35 @@ macro(create_pyside_module)
         --lean-headers
         --api-version=${SUPPORTED_QT_VERSION})
 
+    # check if building for Android with a macOS host
+    # This is not needed for Linux because OpenGLES2 development binaries in
+    # linux can be installed by installing 'libgles2-mesa-dev' package which
+    # comes as a default requirement for building PySide6. As such for
+    # cross-compiling in linux, we use the clang compiler from the installed
+    # libclang itself.
+    if(CMAKE_ANDROID_ARCH_LLVM_TRIPLE AND CMAKE_HOST_APPLE)
+        message(STATUS "Building for Android with arch ${CMAKE_ANDROID_ARCH_LLVM_TRIPLE}")
+        list(APPEND shiboken_command "--clang-option=--target=${CMAKE_ANDROID_ARCH_LLVM_TRIPLE}")
+
+        # CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX does not contain the ANDROID_PLATFORM i.e. it ends with
+        # the form 'aarch64-linux-android-'. Remove the last '-' and add the corresponding clang
+        # based on ANDROID_PLATFORM making it 'aarch64-linux-android26-clang++'
+
+        # Get the length of the string
+        string(LENGTH "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}" _length)
+
+        # Subtract 1 from the length to get the characters till '-'
+        math(EXPR _last_index "${_length} - 1")
+
+        # Get the substring from the start to the character before the last one
+        string(SUBSTRING "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}" 0 "${_last_index}"
+               SHIBOKEN_ANDROID_COMPILER_PREFIX)
+
+        # use the compiler from the Android NDK
+        list(APPEND shiboken_command
+            "--compiler-path=${SHIBOKEN_ANDROID_COMPILER_PREFIX}${CMAKE_ANDROID_API}-clang++")
+    endif()
+
     if(CMAKE_HOST_APPLE)
         set(shiboken_framework_include_dir_list ${QT_FRAMEWORK_INCLUDE_DIR})
         make_path(shiboken_framework_include_dirs ${shiboken_framework_include_dir_list})
@@ -214,6 +253,7 @@ macro(create_pyside_module)
                         DEPENDS ${total_type_system_files}
                                 ${module_GLUE_SOURCES}
                                 ${${module_NAME}_glue_dependency}
+                                ${${module_NAME}_doc_dependency}
                         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                         COMMENT "Running generator for ${module_NAME}...")
 
@@ -311,6 +351,10 @@ macro(create_pyside_module)
                               ${generate_pyi_options})
         add_dependencies("${module_NAME}_pyi" ${module_NAME})
 
+        if(TARGET shibokenmodule)
+            add_dependencies("${module_NAME}_pyi" shibokenmodule)
+        endif()
+
         install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../${module_NAME}.pyi"
                 DESTINATION "${PYTHON_SITE_PACKAGES}/PySide6")
     endif()
@@ -354,4 +398,3 @@ macro(HAS_QT_MODULE var name)
         set("end_${name}" "-->" PARENT_SCOPE)
     endif()
 endmacro()
-
index 23ceda6bd466af39a596ecf1db444fe5ac1e91d1..ad94f278fe8d80c632b8610cd046b61ef3bdc4c6 100644 (file)
@@ -118,6 +118,7 @@ macro(collect_optional_modules)
         SvgWidgets
         DataVisualization
         Graphs
+        GraphsWidgets
         Bluetooth)
     find_package(Qt${QT_MAJOR_VERSION}UiTools)
     if(Qt${QT_MAJOR_VERSION}UiTools_FOUND)
@@ -130,6 +131,11 @@ macro(collect_optional_modules)
     endif()
     list(APPEND ALL_OPTIONAL_MODULES WebChannel WebEngineCore WebEngineWidgets
          WebEngineQuick WebSockets HttpServer)
+    find_package(Qt${QT_MAJOR_VERSION}WebEngineQuick)
+    # for Windows and Linux, QtWebView depends on QtWebEngine to render content
+    if(Qt${QT_MAJOR_VERSION}WebEngineQuick_FOUND OR APPLE)
+        list(APPEND ALL_OPTIONAL_MODULES WebView)
+    endif()
     list(APPEND ALL_OPTIONAL_MODULES 3DCore 3DRender 3DInput 3DLogic 3DAnimation 3DExtras)
 endmacro()
 
@@ -138,13 +144,18 @@ macro(check_os)
     set(ENABLE_MAC "0")
     set(ENABLE_WIN "0")
 
-    if(CMAKE_HOST_APPLE)
-        set(ENABLE_MAC "1")
-    elseif(CMAKE_HOST_WIN32)
-        set(ENABLE_WIN "1")
-        set(ENABLE_UNIX "0")
-    elseif(NOT CMAKE_HOST_UNIX)
-        message(FATAL_ERROR "OS not supported")
+    # check if Android, if so, set ENABLE_UNIX=1
+    # this is needed to avoid including the wrapper specific to macOS when building for Android
+    # from a macOS host
+    if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
+        if(CMAKE_HOST_APPLE)
+            set(ENABLE_MAC "1")
+        elseif(CMAKE_HOST_WIN32)
+            set(ENABLE_WIN "1")
+            set(ENABLE_UNIX "0")
+        elseif(NOT CMAKE_HOST_UNIX)
+            message(FATAL_ERROR "OS not supported")
+        endif()
     endif()
 endmacro()
 
@@ -178,6 +189,12 @@ macro(remove_skipped_modules)
             list(REMOVE_ITEM DISABLED_MODULES ${m})
         endif()
     endforeach()
+
+    # Special list of modules for the __init__.py.in file
+    foreach(im ${all_module_shortnames})
+        list(APPEND init_modules "\"Qt${im}\"")
+    endforeach()
+    string(REPLACE ";" ", " init_modules "${init_modules}")
 endmacro()
 
 macro(collect_module_if_found shortname)
@@ -231,7 +248,17 @@ macro(collect_module_if_found shortname)
     # If the module was found, and also the module path is the same as the
     # Qt5Core base path, we will generate the list with the modules to be installed
     set(looked_in_message ". Looked in: ${${_name_dir}}")
-    if("${${_name_found}}" AND (("${found_basepath}" GREATER "0") OR ("${found_basepath}" EQUAL "0")))
+
+    # 'found_basepath' is used to ensure consistency that all the modules are from the same Qt
+    # directory which prevents issues from arising due to mixing versions or using incompatible Qt
+    # modules. When SHIBOKEN_FORCE_PROCESS_SYSTEM_INCLUDE_PATHS is not empty, we can ignore this
+    # requirement of 'found_basepath'.
+    # This is specifically useful for Flatpak build of PySide6 where For Flatpak the modules are in
+    # different directories. For Flatpak, although the modules are in different directories, they
+    # are all compatible.
+    if("${${_name_found}}" AND
+       ((("${found_basepath}" GREATER "0") OR ("${found_basepath}" EQUAL "0")) OR
+        (NOT SHIBOKEN_FORCE_PROCESS_SYSTEM_INCLUDE_PATHS STREQUAL "")))
         message(STATUS "${module_state} module ${name} found (${ARGN})${looked_in_message}")
         # record the shortnames for the tests
         list(APPEND all_module_shortnames ${shortname})
index 038dc164252ca1946e8d272900942c815be28436..34ec035b71d175c3c2e1f5391a9c82a6b66f8af3 100644 (file)
@@ -223,10 +223,14 @@ if(NOT PYTHON_SITE_PACKAGES)
 endif()
 
 shiboken_get_debug_level(debug_level)
+if(SHIBOKEN_UNOPTIMIZE)
+    SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
 
 set(GENERATOR_EXTRA_FLAGS
     ${SHIBOKEN_GENERATOR_EXTRA_FLAGS}
     ${debug_level}
+    ${UNOPTIMIZE}
     --generator-set=shiboken
     --enable-parent-ctor-heuristic
     --enable-pyside-extensions
@@ -240,4 +244,3 @@ if(SANITIZE_ADDRESS AND NOT MSVC)
 endif()
 
 find_package(Qt6 COMPONENTS Designer)
-
index 83583756f98f1e7e73114f9b4fe8956dd369e4dc..8135c805676a680e90e217b8e8ed454fcfb635ae 100644 (file)
@@ -112,12 +112,26 @@ add_custom_command(OUTPUT "${EXAMPLE_TOOL_TARGET}"
                    WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
                    COMMENT "Generating example gallery...")
 
+# Generate the release notes
+set(RELEASED_NOTES_TARGET "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/release_notes")
+set(RELEASE_NOTES_OPTIONS --target "${RELEASED_NOTES_TARGET}")
+set(RELEASE_NOTES_TOOL "${TOOLS_DIR}/release_notes/main.py")
+if (NOT QUIET_BUILD)
+    list(APPEND RELEASE_NOTES_OPTIONS "-v")
+endif()
+
+add_custom_target("release_notes" DEPENDS "${RELEASED_NOTES_TARGET}")
+add_custom_command(OUTPUT "${RELEASED_NOTES_TARGET}"
+                   COMMAND ${python_executable} ${RELEASE_NOTES_TOOL} ${RELEASE_NOTES_OPTIONS}
+                   WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+                   COMMENT "Generating release notes...")
+
 set(SHIBOKEN_INTERSPHINX_FILE "${ROOT}/pyside6/shiboken6/objects.inv")
 set(HAS_WEBENGINE_WIDGETS 0)
 set(SKIP_SPHINX_WARNINGS 1)
 if (FULLDOCSBUILD)
     set(SKIP_SPHINX_WARNINGS 0)
-    set(SHIBOKEN_INTERSPHINX_FILE "${CMAKE_BINARY_DIR}/doc/html/shiboken6/doc/html/objects.inv")
+    set(SHIBOKEN_INTERSPHINX_FILE "${CMAKE_BINARY_DIR}/doc/html/shiboken6/objects.inv")
     # For Qt modules that are part of the documentation build:
     #    - Configure the module docconf file
     #    - Write shiboken header consisting of pyside6_global.h and module includes
@@ -246,7 +260,7 @@ add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/PySide6/Q
                        --api-version=${SUPPORTED_QT_VERSION}
                        --typesystem-paths="${QDOC_TYPESYSTEM_PATH}"
                        --library-source-dir=${QT_SRC_DIR}
-                       --documentation-data-dir=${DOC_DATA_DIR}/webxml
+                       --documentation-data-dir=${DOC_DATA_DIR}
                        --output-directory=${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}
                        --documentation-code-snippets-dir=${CODE_SNIPPET_ROOT}
                        --snippets-path-rewrite=${QT_ROOT_PATH}:${CODE_SNIPPET_ROOT}
@@ -278,7 +292,7 @@ add_custom_target("licensedocrsts"
     COMMENT "Creating 3rdparty license documentation..."
 )
 
-add_dependencies(docrsts example_gallery)
+add_dependencies(docrsts example_gallery release_notes)
 
 if (FULLDOCSBUILD)
     add_dependencies(apidoc docrsts licensedocrsts)
diff --git a/sources/pyside6/doc/PySide6/QtAsyncio/coroutines.png b/sources/pyside6/doc/PySide6/QtAsyncio/coroutines.png
new file mode 100644 (file)
index 0000000..3fa00c8
Binary files /dev/null and b/sources/pyside6/doc/PySide6/QtAsyncio/coroutines.png differ
index 326f6efccb6f2c4e5591c2bd4372e4e1a27b884c..e6b2ca147d1c4e736bd54477a94cb3508b4648f2 100644 (file)
@@ -118,6 +118,9 @@ or
 to run the coroutine and then stop the event loop upon its completion.
 This latter case behaves identically to ``asyncio.run(my_coroutine())``.
 
+If there is no instance of a QCoreApplication, QGuiApplication or
+QApplication yet, a new instance of QCoreApplication is created.
+
 An additional optional argument ``quit_qapp`` can be passed to ``run()``
 to configure whether the QCoreApplication at the core of QtAsyncio
 should be shut down when asyncio finishes. A special case where one
@@ -141,10 +144,10 @@ Coroutines explained
 
 Coroutines are functions that can be paused (yield) and resumed. Behind
 this simple concept lies a complex mechanism that is abstracted by the
-asynchronous framework. This talk presents a diagram that attempts to
-illustrate the flow of a coroutine from the moment it's provided to the
-async framework until it's completed.
+asynchronous framework. `This talk <https://www.youtube.com/watch?v=XuqdTvisqkQ>`_
+presents the below diagram that attempts to illustrate the flow of a
+coroutine from the moment it's provided to the async framework until
+it's completed.
 
-.. image:: https://img.youtube.com/vi/XuqdTvisqkQ/mqdefault.jpg
-    :alt: Asynchronous programming with asyncio and Qt
-    :target: https://www.youtube.com/watch?v=XuqdTvisqkQ
+.. image:: coroutines.png
+   :alt: Coroutines explained
index 08c4646c62bd6fdf05aa071cf55a25741c42f143..f9eedd9f7059ec116e39def323b59eed578ddcd8 100644 (file)
@@ -98,3 +98,85 @@ dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.
   font-size: +1rem;
   color: var(--color-brand-primary);
 }
+
+article[role="main"] section[id^="pyside6-"] h1 {
+    font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt em span.pre{
+    font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt span.pre {
+    font-size: 1.5em;
+}
+
+.prominent-summary {
+    font-size: 1em;
+    font-weight: bold;
+    color: #2a9d8f;
+    background-color: #f0f0f0;
+    padding: 5px;
+    border-radius: 5px;
+}
+
+section#examples .sd-card {
+  transition: transform 0.2s, box-shadow 0.2s;
+}
+
+section#examples .sd-card-title {
+  font-size: 1.25em;
+  font-weight: bold;
+}
+
+section#examples .sd-card-body{
+  font-size: 1em;
+}
+
+/* Example card hover */
+section#examples .cover-img img:hover {
+    background-color: var(--color-background);
+    opacity: 0.3;
+}
+
+section#examples .sd-card {
+    z-index: 1;
+    position: relative;
+}
+
+section#examples .sd-card-hover:hover {
+    background-color: var(--color-background);
+    transform: translateY(-5px);
+    box-shadow: 0 4px 8px rgba(27, 31, 35, 0.12), 0 12px 48px rgba(27, 31, 35, 0.24);
+}
+
+section#examples .sd-card-hover:hover .sd-card-footer {
+    bottom: 0;
+    display: block;
+    left: 0;
+    position: absolute;
+    color: var(--color-background-font);
+    right: 0;
+    top: 0;
+    padding: 20px;
+    opacity: 1;
+    z-index: 5;
+    background-color: var(--color-background);
+}
+
+section#examples .sd-card-hover:hover a {
+    z-index: 7;
+}
+
+section#examples details .sd-card .sd-card-footer {
+    display: none;
+}
+
+section#examples details .sd-card-body p strong {
+    display: block;
+    font-size: 1.5em;
+}
+
+section#examples details .sd-card-title {
+    font-size: 1.1em;
+}
index 08a30fc0f93ed04e884d0f4c7abe238c00d29453..0fdf2041aff804e99ea745f13f9efe42da63ee0a 100644 (file)
@@ -1,3 +1,5 @@
+.. _sphx_tag_android:
+
 My tags: Android
 ################
 
index 81da58370fc3f5a556425252a1453e48af137e8f..700647a7904578bca206107ad84773c3e3aa8847 100644 (file)
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
-# see README.md
 # A line enclosed in [] denotes a (relative) target directory
 
 [overviews]
-animation-overview.webxml
-animation.webxml
-application-windows.webxml
-audiooverview.webxml
-bindableproperties.webxml
-bluetooth-examples.webxml
-cameraoverview.webxml
-compatmap.webxml
-concurrent-changes-qt6.webxml
-containers.webxml
-coordsys.webxml
-creating-a-sensor-plugin.webxml
-custom-types.webxml
-database.webxml
-datastreamformat.webxml
-dbus-changes-qt6.webxml
-determining-the-default-sensor-for-a-type.webxml
-dialogs.webxml
-dnd.webxml
-dynamic-sensor-backend-registration.webxml
-echoclient.webxml
-echoserver.webxml
-events.webxml
-eventsandfilters.webxml
-examples-dbus.webxml
-examples-designer.webxml
-examples-desktop.webxml
-examples-dialogs.webxml
-examples-graphicsview.webxml
-examples-itemviews.webxml
-examples-mainwindow.webxml
-examples-network.webxml
-examples-painting.webxml
-examples-qthelp.webxml
-examples-qtnetworkauth.webxml
-examples-qtscxml.webxml
-examples-qtuitools.webxml
-examples-richtext.webxml
-examples-widgets-opengl.webxml
-examples-widgets.webxml
-focus.webxml
-gallery.webxml
-genericbackend.webxml
-gestures-overview.webxml
-graphicsview.webxml
-gui-changes-qt6.webxml
-helpsystem.webxml
-implicit-sharing.webxml
-io-functions.webxml
-io.webxml
-java-style-iterators.webxml
-json.webxml
-layout.webxml
-mainwindow.webxml
-metaobjects.webxml
-model-view-programming.webxml
-modelview.webxml
-multimedia-examples.webxml
-multimediaoverview.webxml
-network-changes-qt6.webxml
-network.webxml
-nfc-android.webxml
-nfc-examples.webxml
-object.webxml
-objecttrees.webxml
-opengl-changes-qt6.webxml
-painting-3d.webxml
-painting.webxml
-paintsystem-devices.webxml
-paintsystem-drawing.webxml
-paintsystem-images.webxml
-paintsystem.webxml
-plugins.webxml
-position-plugin-geoclue2.webxml
-position-plugin-gypsy.webxml
-position-plugin-nmea.webxml
-positioning-cpp-qml.webxml
-printing.webxml
-printsupport-changes-qt6.webxml
-properties.webxml
-qabstracteventdispatcher-timerinfo.webxml
-qabstracttextdocumentlayout-paintcontext.webxml
-qabstracttextdocumentlayout-selection.webxml
-qaccessible-state.webxml
-qadoptshareddatatag.webxml
-qcborerror.webxml
-qcborparsererror.webxml
-qcborstreamreader-stringresult.webxml
-qdbusdeclaringsignals.webxml
-qdbusdeclaringslots.webxml
-qdbustypesystem.webxml
-qdbusviewer.webxml
-qdbusxml2cpp.webxml
-qdrawutil-h.webxml
-qdtlsclientverifier-generatorparameters.webxml
-qformlayout-takerowresult.webxml
-qglobalstatic.webxml
-qhashseed.webxml
-qhelplink.webxml
-qiconengine-scaledpixmapargument.webxml
-qiterator.webxml
-qjsonparseerror.webxml
-qmediatimerange-interval.webxml
-qml-advtutorial.webxml
-qml-changes-qt6.webxml
-qml-dynamicview-tutorial.webxml
-qml-tutorial.webxml
-qml-tutorial1.webxml
-qml-tutorial2.webxml
-qml-tutorial3.webxml
-qmldiskcache.webxml
-qmlreference.webxml
-qndeffilter-record.webxml
-qoutputrange.webxml
-qpageranges-range.webxml
-qprocess-createprocessarguments.webxml
-qqmlcontext-propertypair.webxml
-qquickitem-itemchangedata.webxml
-qscxmlc.webxml
-qsggeometry-attribute.webxml
-qsggeometry-coloredpoint2d.webxml
-qsggeometry-point2d.webxml
-qsggeometry-texturedpoint2d.webxml
-qsgmaterialshader-graphicspipelinestate.webxml
-qsgmaterialtype.webxml
-qstaticplugin.webxml
-qt3d-basicshapes-cpp-example.webxml
-qt3d-changes-qt6.webxml
-qt3d-cpp.webxml
-qt3d-examples.webxml
-qt3d-multiviewport-example.webxml
-qt3d-overview.webxml
-qt3d-pbr-materials-example.webxml
-qt3d-qml.webxml
-qt3d-simple-qml-example.webxml
-qt3d-simplecustommaterial-example.webxml
-qt3d-wireframe-example.webxml
-qt3drender-framegraph.webxml
-qt3drender-geometry.webxml
-qt3drender-porting-to-rhi.webxml
-qt3drender-protips.webxml
-qtalgorithms.webxml
-qtbluetooth-btchat-example.webxml
-qtbluetooth-changes-qt6.webxml
-qtbluetooth-le-overview.webxml
-qtbluetooth-lowenergyscanner-example.webxml
-qtbluetooth-overview.webxml
-qtcborcommon.webxml
-qtcharts-barmodelmapper-example.webxml
-qtcharts-boxplotchart-example.webxml
-qtcharts-candlestickchart-example.webxml
-qtcharts-changes-qt6.webxml
-qtcharts-customchart-example.webxml
-qtcharts-datetimeaxis-example.webxml
-qtcharts-examples.webxml
-qtcharts-horizontalbarchart-example.webxml
-qtcharts-horizontalpercentbarchart-example.webxml
-qtcharts-horizontalstackedbarchart-example.webxml
-qtcharts-legendmarkers-example.webxml
-qtcharts-multiaxis-example.webxml
-qtcharts-openglseries-example.webxml
-qtcharts-overview.webxml
-qtcharts-pointsselectionandmarkers-example.webxml
-qtcharts-polarchart-example.webxml
-qtcharts-qmlaxes-example.webxml
-qtcharts-qmlcustomizations-example.webxml
-qtcharts-qmlcustomlegend-example.webxml
-qtcharts-qmlf1legends-example.webxml
-qtcharts-qmloscilloscope-example.webxml
-qtcharts-qmlweather-example.webxml
-qtcharts-scatterchart-example.webxml
-qtcharts-selectedbar-example.webxml
-qtcharts-splinechart-example.webxml
-qtcharts-stackedbarchart-example.webxml
-qtcharts-zoomlinechart-example.webxml
-qtconcurrent-imagescaling-example.webxml
-qtconcurrent-wordcount-example.webxml
-qtconcurrentfilter.webxml
-qtconcurrentmap.webxml
-qtconcurrentrun.webxml
-qtconcurrenttask.webxml
-qtcore-changes-qt6.webxml
-qtcore-ipc-localfortuneclient-example.webxml
-qtcore-ipc-localfortuneserver-example.webxml
-qtcore-platform-androidnotifier-example.webxml
-qtcore-serialization-savegame-example.webxml
-qtcore-threads-queuedcustomtype-example.webxml
-qtcore-threads-semaphores-example.webxml
-qtcore-threads-waitconditions-example.webxml
-qtcore-tools-contiguouscache-example.webxml
-qtdbus-chat-example.webxml
-qtdbus-cmake-qt-add-dbus-adaptor.webxml
-qtdbus-cmake-qt-add-dbus-interface.webxml
-qtdbus-cmake-qt-add-dbus-interfaces.webxml
-qtdbus-cmake-qt-generate-dbus-interface.webxml
-qtdbus-complexpingpong-example.webxml
-qtdbus-remotecontrolledcar-example.webxml
-qtdesigner-calculatorbuilder-example.webxml
-qtdesigner-calculatorform-example.webxml
-qtdesigner-components.webxml
-qtdesigner-containerextension-example.webxml
-qtdesigner-customwidgetplugin-example.webxml
-qtdesigner-manual.webxml
-qtest-overview.webxml
-qtest-tutorial.webxml
-qtextedit-extraselection.webxml
-qtextlayout-formatrange.webxml
-qtextoption-tab.webxml
-qtglobal.webxml
-qtgui-rasterwindow-example.webxml
-qthelp-contextsensitivehelp-example.webxml
-qthelp-framework.webxml
-qthelpproject.webxml
-qtilerules.webxml
-qtimezone-offsetdata.webxml
-qtjavascript.webxml
-qtmath.webxml
-qtmultimedia-apple.webxml
-qtmultimedia-changes-qt6.webxml
-qtnetwork-broadcastreceiver-example.webxml
-qtnetwork-broadcastsender-example.webxml
-qtnetwork-http-example.webxml
-qtnetwork-multicastreceiver-example.webxml
-qtnetwork-multicastsender-example.webxml
-qtnetwork-network-chat-example.webxml
-qtnetwork-programming.webxml
-qtnetwork-securesocketclient-example.webxml
-qtnetwork-secureudpclient-example.webxml
-qtnetwork-secureudpserver-example.webxml
-qtnetwork-torrent-example.webxml
-qtnfc-annotatedurl-example.webxml
-qtnfc-changes-qt6.webxml
-qtnfc-ndefeditor-example.webxml
-qtnfc-overview.webxml
-qtopengl-2dpainting-example.webxml
-qtopengl-cube-example.webxml
-qtopengl-hellogles3-example.webxml
-qtopengl-openglwindow-example.webxml
-qtplugin.webxml
-qtpositioning-android.webxml
-qtpositioning-changes-qt6.webxml
-qtpositioning-examples.webxml
-qtpositioning-ios.webxml
-qtpositioning-logfilepositionsource-example.webxml
-qtpositioning-plugins.webxml
-qtpositioning-satelliteinfo-example.webxml
-qtpositioning-weatherinfo-example.webxml
-qtqml-documents-definetypes.webxml
-qtqml-documents-networktransparency.webxml
-qtqml-documents-scope.webxml
-qtqml-documents-structure.webxml
-qtqml-documents-topic.webxml
-qtqml-dynamicscene-example.webxml
-qtqml-javascript-dynamicobjectcreation.webxml
-qtqml-javascript-expressions.webxml
-qtqml-javascript-finetuning.webxml
-qtqml-javascript-functionlist.webxml
-qtqml-javascript-hostenvironment.webxml
-qtqml-javascript-imports.webxml
-qtqml-javascript-qmlglobalobject.webxml
-qtqml-javascript-resources.webxml
-qtqml-javascript-topic.webxml
-qtqml-qml-i18n-example.webxml
-qtqml-syntax-basics.webxml
-qtqml-syntax-directoryimports.webxml
-qtqml-syntax-imports.webxml
-qtqml-syntax-objectattributes.webxml
-qtqml-syntax-propertybinding.webxml
-qtqml-tutorials-extending-qml-example.webxml
-qtqml-typesystem-basictypes.webxml
-qtqml-typesystem-objecttypes.webxml
-qtqml-typesystem-topic.webxml
-qtquick-animation-example.webxml
-qtquick-bestpractices.webxml
-qtquick-canvas-example.webxml
-qtquick-codesamples.webxml
-qtquick-convenience-topic.webxml
-qtquick-cppextensionpoints.webxml
-qtquick-customitems-dialcontrol-example.webxml
-qtquick-customitems-flipable-example.webxml
-qtquick-customitems-painteditem-example.webxml
-qtquick-draganddrop-example.webxml
-qtquick-effects-particles.webxml
-qtquick-effects-sprites.webxml
-qtquick-effects-topic.webxml
-qtquick-effects-transformations.webxml
-qtquick-imageelements-example.webxml
-qtquick-imageprovider-example.webxml
-qtquick-imageresponseprovider-example.webxml
-qtquick-input-focus.webxml
-qtquick-input-mouseevents.webxml
-qtquick-input-textinput.webxml
-qtquick-input-topic.webxml
-qtquick-keyinteraction-example.webxml
-qtquick-layouts-example.webxml
-qtquick-localstorage-example.webxml
-qtquick-models-abstractitemmodel-example.webxml
-qtquick-modelviewsdata-cppmodels.webxml
-qtquick-modelviewsdata-modelview.webxml
-qtquick-modelviewsdata-topic.webxml
-qtquick-mousearea-example.webxml
-qtquick-particles-affectors-example.webxml
-qtquick-particles-emitters-example.webxml
-qtquick-particles-imageparticle-example.webxml
-qtquick-particles-performance.webxml
-qtquick-particles-system-example.webxml
-qtquick-positioners-example.webxml
-qtquick-positioning-anchors.webxml
-qtquick-positioning-layouts.webxml
-qtquick-positioning-righttoleft.webxml
-qtquick-positioning-topic.webxml
-qtquick-quick-accessibility-example.webxml
-qtquick-quickwidgets-quickwidget-example.webxml
-qtquick-scenegraph-custommaterial-example.webxml
-qtquick-scenegraph-d3d11underqml-example.webxml
-qtquick-scenegraph-graph-example.webxml
-qtquick-scenegraph-materials.webxml
-qtquick-scenegraph-metaltextureimport-example.webxml
-qtquick-scenegraph-metalunderqml-example.webxml
-qtquick-scenegraph-nodes.webxml
-qtquick-scenegraph-twotextureproviders-example.webxml
-qtquick-scenegraph-vulkantextureimport-example.webxml
-qtquick-scenegraph-vulkanunderqml-example.webxml
-qtquick-shadereffects-example.webxml
-qtquick-shapes-example.webxml
-qtquick-statesanimations-animations.webxml
-qtquick-statesanimations-behaviors.webxml
-qtquick-statesanimations-states.webxml
-qtquick-statesanimations-topic.webxml
-qtquick-tableview-gameoflife-example.webxml
-qtquick-tableview-pixelator-example.webxml
-qtquick-text-example.webxml
-qtquick-tool-qmllint.webxml
-qtquick-tools-and-utilities.webxml
-qtquick-tutorials-dynamicview-dynamicview1-example.webxml
-qtquick-tutorials-dynamicview-dynamicview2-example.webxml
-qtquick-tutorials-dynamicview-dynamicview3-example.webxml
-qtquick-tutorials-dynamicview-dynamicview4-example.webxml
-qtquick-tutorials-samegame-samegame1-example.webxml
-qtquick-tutorials-samegame-samegame2-example.webxml
-qtquick-tutorials-samegame-samegame3-example.webxml
-qtquick-tutorials-samegame-samegame4-example.webxml
-qtquick-views-example.webxml
-qtquick-visualcanvas-adaptations-openvg.webxml
-qtquick-visualcanvas-adaptations-software.webxml
-qtquick-visualcanvas-adaptations.webxml
-qtquick-visualcanvas-coordinates.webxml
-qtquick-visualcanvas-scenegraph-renderer.webxml
-qtquick-visualcanvas-scenegraph.webxml
-qtquick-visualcanvas-topic.webxml
-qtquick-visualcanvas-visualparent.webxml
-qtquick-visualtypes-topic.webxml
-qtquickcontrols-changes-qt6.webxml
-qtquickcontrols-chattutorial-example.webxml
-qtquickcontrols-contactlist-example.webxml
-qtquickcontrols-eventcalendar-example.webxml
-qtquickcontrols-flatstyle-example.webxml
-qtquickcontrols-imagine-automotive-example.webxml
-qtquickcontrols-texteditor-example.webxml
-qtquickcontrols-wearable-example.webxml
-qtquickcontrols-basic.webxml
-qtquickcontrols-buttons.webxml
-qtquickcontrols-configuration.webxml
-qtquickcontrols-containers.webxml
-qtquickcontrols-customize.webxml
-qtquickcontrols-delegates.webxml
-qtquickcontrols-deployment.webxml
-qtquickcontrols-environment.webxml
-qtquickcontrols-examples.webxml
-qtquickcontrols-fileselectors.webxml
-qtquickcontrols-focus.webxml
-qtquickcontrols-fusion.webxml
-qtquickcontrols-gettingstarted.webxml
-qtquickcontrols-guidelines.webxml
-qtquickcontrols-icons.webxml
-qtquickcontrols-imagine.webxml
-qtquickcontrols-indicators.webxml
-qtquickcontrols-input.webxml
-qtquickcontrols-macos.webxml
-qtquickcontrols-material.webxml
-qtquickcontrols-menus.webxml
-qtquickcontrols-navigation.webxml
-qtquickcontrols-popups.webxml
-qtquickcontrols-separators.webxml
-qtquickcontrols-styles.webxml
-qtquickcontrols-universal.webxml
-qtquickcontrols-windows.webxml
-qtquicklayouts-overview.webxml
-qtremoteobjects-cmake-qt-add-repc-merged.webxml
-qtremoteobjects-cmake-qt-add-repc-replicas.webxml
-qtremoteobjects-cmake-qt-add-repc-sources.webxml
-qtremoteobjects-cmake-qt-rep-from-headers.webxml
-qtremoteobjects-compatibility.webxml
-qtremoteobjects-custom-transport.webxml
-qtremoteobjects-external-schemas.webxml
-qtremoteobjects-gettingstarted.webxml
-qtremoteobjects-interaction.webxml
-qtremoteobjects-node.webxml
-qtremoteobjects-registry.webxml
-qtremoteobjects-repc.webxml
-qtremoteobjects-replica.webxml
-qtremoteobjects-source.webxml
-qtremoteobjects-ssl-example.webxml
-qtremoteobjects-troubleshooting.webxml
-qtremoteobjects-websockets-example.webxml
-qtscxml-changes-qt6.webxml
-qtscxml-cmake-qt-add-statecharts.webxml
-qtscxml-ftpclient-example.webxml
-qtscxml-instantiating-state-machines.webxml
-qtscxml-overview.webxml
-qtscxml-scxml-compliance.webxml
-qtscxml-sudoku-example.webxml
-qtscxml-trafficlight-qml-dynamic-example.webxml
-qtscxml-trafficlight-qml-simple-example.webxml
-qtscxml-trafficlight-qml-static-example.webxml
-qtscxml-trafficlight-widgets-dynamic-example.webxml
-qtscxml-trafficlight-widgets-static-example.webxml
-qtsensors-changes-qt6.webxml
-qtsensors-cpp.webxml
-qtsensors-examples.webxml
-qtsensors-sensorsshowcase-example.webxml
-qtserialport-blockingreceiver-example.webxml
-qtserialport-blockingsender-example.webxml
-qtserialport-changes-qt6.webxml
-qtserialport-examples.webxml
-qtserialport-terminal-example.webxml
-qtsql-cachedtable-example.webxml
-qtsql-changes-qt6.webxml
-qtsql-drilldown-example.webxml
-qtsql-masterdetail-example.webxml
-qtsql-querymodel-example.webxml
-qtsql-sqlbrowser-example.webxml
-qtsql-sqlwidgetmapper-example.webxml
-qtsql-tablemodel-example.webxml
-qtsvg-changes-qt6.webxml
-qttest-best-practices-qdoc.webxml
-qttestlib-tutorial1-example.webxml
-qttestlib-tutorial2-example.webxml
-qttestlib-tutorial3-example.webxml
-qttestlib-tutorial4-example.webxml
-qttestlib-tutorial5-example.webxml
-qttestlib-tutorial6.webxml
-qtuitools-textfinder-example.webxml
-qtwebchannel-changes-qt6.webxml
-qtwebchannel-chatclient-html-example.webxml
-qtwebchannel-chatclient-qml-example.webxml
-qtwebchannel-chatserver-cpp-example.webxml
-qtwebchannel-examples.webxml
-qtwebchannel-javascript.webxml
-qtwebengine-changes-qt6.webxml
-qtwebengine-features.webxml
-qtwebengine-overview.webxml
-qtwebengine-platform-notes.webxml
-qtwebengine-webenginequick-lifecycle-example.webxml
-qtwebengine-webenginewidgets-contentmanipulation-example.webxml
-qtwebengine-webenginewidgets-cookiebrowser-example.webxml
-qtwebengine-webenginewidgets-html2pdf-example.webxml
-qtwebengine-webenginewidgets-maps-example.webxml
-qtwebengine-webenginewidgets-printme-example.webxml
-qtwebengine-webenginewidgets-spellchecker-example.webxml
-qtwebengine-webenginewidgets-videoplayer-example.webxml
-qtwebenginewidgets-qtwebkitportingguide.webxml
-qtwebsockets-changes-qt6.webxml
-qtwebsockets-echoclient-example.webxml
-qtwebsockets-echoserver-example.webxml
-qtwebsockets-examples.webxml
-qtwebsockets-qmlwebsocketclient-example.webxml
-qtwebsockets-qmlwebsocketserver-example.webxml
-qtwebsockets-simplechat-example.webxml
-qtwebsockets-sslechoclient-example.webxml
-qtwebsockets-sslechoserver-example.webxml
-qtwebsockets-testing.webxml
-qtwidgets-gestures-imagegestures-example.webxml
-qtwidgets-graphicsview-basicgraphicslayouts-example.webxml
-qtwidgets-graphicsview-chip-example.webxml
-qtwidgets-graphicsview-simpleanchorlayout-example.webxml
-qtwidgets-itemviews-coloreditorfactory-example.webxml
-qtwidgets-itemviews-combowidgetmapper-example.webxml
-qtwidgets-itemviews-customsortfiltermodel-example.webxml
-qtwidgets-itemviews-frozencolumn-example.webxml
-qtwidgets-itemviews-simpletreemodel-example.webxml
-qtwidgets-mainwindows-menus-example.webxml
-qtwidgets-painting-affine-example.webxml
-qtwidgets-painting-composition-example.webxml
-qtwidgets-painting-deform-example.webxml
-qtwidgets-painting-gradients-example.webxml
-qtwidgets-painting-imagecomposition-example.webxml
-qtwidgets-painting-painterpaths-example.webxml
-qtwidgets-painting-pathstroke-example.webxml
-qtwidgets-painting-transformations-example.webxml
-qtwidgets-tools-completer-example.webxml
-qtwidgets-tools-customcompleter-example.webxml
-qtwidgets-tools-echoplugin-example.webxml
-qtwidgets-tools-settingseditor-example.webxml
-qtwidgets-tools-styleplugin-example.webxml
-qtwidgets-tools-treemodelcompleter-example.webxml
-qtwidgets-tools-undoframework-example.webxml
-qtwidgets-touch-knobs-example.webxml
-qtwidgets-tutorials-notepad-example.webxml
-qtwidgets-tutorials-widgets-childwidget-example.webxml
-qtwidgets-tutorials-widgets-nestedlayouts-example.webxml
-qtwidgets-tutorials-widgets-toplevel-example.webxml
-qtwidgets-tutorials-widgets-windowlayout-example.webxml
-qtwidgets-widgets-analogclock-example.webxml
-qtwidgets-widgets-calculator-example.webxml
-qtwidgets-widgets-calendarwidget-example.webxml
-qtwidgets-widgets-groupbox-example.webxml
-qtwidgets-widgets-lineedits-example.webxml
-qtwidgets-widgets-scribble-example.webxml
-qtwidgets-widgets-shapedclock-example.webxml
-qtwidgets-widgets-sliders-example.webxml
-qtwidgets-widgets-spinboxes-example.webxml
-qtwidgets-widgets-tablet-example.webxml
-qtwidgets-widgets-windowflags-example.webxml
-quick-changes-qt6.webxml
-qwebenginecookiestore-filterrequest.webxml
-qwidget-styling.webxml
-remoteobjects-changes-qt6.webxml
-remoteobjects-example-dynamic-replica.webxml
-remoteobjects-example-registry.webxml
-remoteobjects-example-static-source.webxml
-resources.webxml
-richtext-advanced-processing.webxml
-richtext-common-tasks.webxml
-richtext-cursor.webxml
-richtext-html-subset.webxml
-richtext-layouts.webxml
-richtext-processing.webxml
-richtext-structure.webxml
-richtext.webxml
-senorfwbackend.webxml
-sensors-backend-topics.webxml
-shared.webxml
-signalsandslots.webxml
-sql-connecting.webxml
-sql-driver.webxml
-sql-forms.webxml
-sql-model.webxml
-sql-presenting.webxml
-sql-programming.webxml
-sql-sqlstatements.webxml
-sql-types.webxml
-ssl.webxml
-standard-dialogs.webxml
-style-reference.webxml
-stylesheet-customizing.webxml
-stylesheet-designer.webxml
-stylesheet-examples.webxml
-stylesheet-reference.webxml
-stylesheet-syntax.webxml
-stylesheet.webxml
-svgrendering.webxml
-testlib-changes-qt6.webxml
-timers.webxml
-usingadaptors.webxml
-videooverview.webxml
-webengine-examples.webxml
-webengine-widgetexamples.webxml
-websockets-overview.webxml
-widget-classes.webxml
-widgets-changes-qt6.webxml
-widgets-tutorial.webxml
-xml-changes-qt6.webxml
-xml-dom-tml.webxml
-xml-namespaces.webxml
-xml-processing.webxml
-xml-streaming.webxml
-xml-tools.webxml
-# qtdoc repository
-accessible.webxml
-appicon.webxml
-create-your-first-applications.webxml
-deployment.webxml
-desktop-integration.webxml
-exceptionsafety.webxml
-explore-qt.webxml
-get-and-install-qt.webxml
-gettingstarted.webxml
-highdpi.webxml
-install-qt-design-studio.webxml
-ipc.webxml
-known-issues.webxml
-overviews-main.webxml
-plugins-howto.webxml
-qml-codingconventions.webxml
-qml-glossary.webxml
-qmlapplications.webxml
-qmlfirststeps.webxml
-qt-intro.webxml
-qtquick-debugging.webxml
-qtquick-performance.webxml
-qtquick-qml-runtime.webxml
-qtquick-usecase-animations.webxml
-qtquick-usecase-integratingjs.webxml
-qtquick-usecase-layouts.webxml
-qtquick-usecase-styling.webxml
-qtquick-usecase-text.webxml
-qtquick-usecase-userinput.webxml
-qtquick-usecase-visual.webxml
-qundo.webxml
-rcc.webxml
-restoring-geometry.webxml
-scalability.webxml
-session.webxml
-sharedlibrary.webxml
-solutions-for-application-development.webxml
-solutions-for-ui-design.webxml
-thread-basics.webxml
-threads.webxml
-tools-for-qt-quick-uis.webxml
-tools-for-qt-widget-based-uis.webxml
-topics-app-development.webxml
-topics-data-io.webxml
-topics-ui.webxml
-uic.webxml
-unicode.webxml
-wayland-and-qt.webxml
+qt3d/webxml/qt3d-changes-qt6.webxml
+qt3d/webxml/qt3d-cpp.webxml
+qt3d/webxml/qt3d-overview.webxml
+qt3d/webxml/qt3d-qml.webxml
+qt3d/webxml/qt3drender-framegraph.webxml
+qt3d/webxml/qt3drender-geometry.webxml
+qt3d/webxml/qt3drender-porting-to-rhi.webxml
+qt3d/webxml/qt3drender-protips.webxml
+qtbluetooth/webxml/qtbluetooth-changes-qt6.webxml
+qtbluetooth/webxml/qtbluetooth-le-overview.webxml
+qtbluetooth/webxml/qtbluetooth-overview.webxml
+qtcharts/webxml/qtcharts-changes-qt6.webxml
+qtcharts/webxml/qtcharts-overview.webxml
+qtconcurrent/webxml/concurrent-changes-qt6.webxml
+qtconcurrent/webxml/qtconcurrentfilter.webxml
+qtconcurrent/webxml/qtconcurrentmap.webxml
+qtconcurrent/webxml/qtconcurrentrun.webxml
+qtconcurrent/webxml/qtconcurrenttask.webxml
+qtcore/webxml/animation-overview.webxml
+qtcore/webxml/bindableproperties.webxml
+qtcore/webxml/cbor.webxml
+qtcore/webxml/containers.webxml
+qtcore/webxml/custom-types.webxml
+qtcore/webxml/datastreamformat.webxml
+qtcore/webxml/eventsandfilters.webxml
+qtcore/webxml/implicit-sharing.webxml
+qtcore/webxml/io-functions.webxml
+qtcore/webxml/ipc.webxml
+qtcore/webxml/java-style-iterators.webxml
+qtcore/webxml/json.webxml
+qtcore/webxml/metaobjects.webxml
+qtcore/webxml/objecttrees.webxml
+qtcore/webxml/object.webxml
+qtcore/webxml/permissions.webxml
+qtcore/webxml/plugins.webxml
+qtcore/webxml/properties.webxml
+qtcore/webxml/qabstracteventdispatcher-timerinfo.webxml
+qtcore/webxml/qadoptshareddatatag.webxml
+qtcore/webxml/qcborerror.webxml
+qtcore/webxml/qcborparsererror.webxml
+qtcore/webxml/qcborstreamreader-stringresult.webxml
+qtcore/webxml/qglobalstatic.webxml
+qtcore/webxml/qhashseed.webxml
+qtcore/webxml/qiterator.webxml
+qtcore/webxml/qjsonparseerror.webxml
+qtcore/webxml/qprocess-createprocessarguments.webxml
+qtcore/webxml/qstaticplugin.webxml
+qtcore/webxml/qtalgorithms.webxml
+qtcore/webxml/qtcborcommon.webxml
+qtcore/webxml/qtcore-changes-qt6.webxml
+qtcore/webxml/qtglobal.webxml
+qtcore/webxml/qtimezone-offsetdata.webxml
+qtcore/webxml/qtmath.webxml
+qtcore/webxml/qtplugin.webxml
+qtcore/webxml/resources.webxml
+qtcore/webxml/shared.webxml
+qtcore/webxml/signalsandslots.webxml
+qtcore/webxml/timers.webxml
+qtdbus/webxml/dbus-changes-qt6.webxml
+qtdbus/webxml/qdbusdeclaringsignals.webxml
+qtdbus/webxml/qdbusdeclaringslots.webxml
+qtdbus/webxml/qdbustypesystem.webxml
+qtdbus/webxml/qdbusviewer.webxml
+qtdbus/webxml/qdbusxml2cpp.webxml
+qtdbus/webxml/qtdbus-cmake-qt-add-dbus-adaptor.webxml
+qtdbus/webxml/qtdbus-cmake-qt-add-dbus-interfaces.webxml
+qtdbus/webxml/qtdbus-cmake-qt-add-dbus-interface.webxml
+qtdbus/webxml/qtdbus-cmake-qt-generate-dbus-interface.webxml
+qtdbus/webxml/usingadaptors.webxml
+qtdesigner/webxml/qtdesigner-components.webxml
+qtdesigner/webxml/qtdesigner-manual.webxml
+qtdoc/webxml/accessible.webxml
+qtdoc/webxml/appicon.webxml
+qtdoc/webxml/create-your-first-applications.webxml
+qtdoc/webxml/deployment.webxml
+qtdoc/webxml/desktop-integration.webxml
+qtdoc/webxml/exceptionsafety.webxml
+qtdoc/webxml/explore-qt.webxml
+qtdoc/webxml/get-and-install-qt.webxml
+qtdoc/webxml/gettingstarted.webxml
+qtdoc/webxml/highdpi.webxml
+qtdoc/webxml/install-qt-design-studio.webxml
+qtdoc/webxml/known-issues.webxml
+qtdoc/webxml/mobiledevelopment.webxml
+qtdoc/webxml/overviews-main.webxml
+qtdoc/webxml/plugins-howto.webxml
+qtdoc/webxml/qmlapplications.webxml
+qtdoc/webxml/qml-codingconventions.webxml
+qtdoc/webxml/qmlfirststeps.webxml
+qtdoc/webxml/qml-glossary.webxml
+qtdoc/webxml/qt-intro.webxml
+qtdoc/webxml/qtlanguages.webxml
+qtdoc/webxml/qtquick-debugging.webxml
+qtdoc/webxml/qtquick-performance.webxml
+qtdoc/webxml/qtquick-qml-runtime.webxml
+qtdoc/webxml/qtquick-usecase-animations.webxml
+qtdoc/webxml/qtquick-usecase-integratingjs.webxml
+qtdoc/webxml/qtquick-usecase-layouts.webxml
+qtdoc/webxml/qtquick-usecase-styling.webxml
+qtdoc/webxml/qtquick-usecase-text.webxml
+qtdoc/webxml/qtquick-usecase-userinput.webxml
+qtdoc/webxml/qtquick-usecase-visual.webxml
+qtdoc/webxml/qt-releases.webxml
+qtdoc/webxml/qundo.webxml
+qtdoc/webxml/rcc.webxml
+qtdoc/webxml/restoring-geometry.webxml
+qtdoc/webxml/scalability.webxml
+qtdoc/webxml/security.webxml
+qtdoc/webxml/session.webxml
+qtdoc/webxml/sharedlibrary.webxml
+qtdoc/webxml/solutions-for-application-development.webxml
+qtdoc/webxml/solutions-for-ui-design.webxml
+qtdoc/webxml/testing-and-debugging.webxml
+qtdoc/webxml/thread-basics.webxml
+qtdoc/webxml/threads.webxml
+qtdoc/webxml/tools-for-qt-quick-uis.webxml
+qtdoc/webxml/tools-for-qt-widget-based-uis.webxml
+qtdoc/webxml/topics-app-development.webxml
+qtdoc/webxml/topics-core.webxml
+qtdoc/webxml/topics-data-io.webxml
+qtdoc/webxml/topics-graphics.webxml
+qtdoc/webxml/topics-network-connectivity.webxml
+qtdoc/webxml/topics-ui.webxml
+qtdoc/webxml/uic.webxml
+qtdoc/webxml/unicode.webxml
+qtdoc/webxml/untrusteddata.webxml
+qtdoc/webxml/wayland-and-qt.webxml
+qtgraphs/webxml/qtgraphs-and-qtquick3d-integration-guide.webxml
+qtgraphs/webxml/qtgraphs-configure-options.webxml
+qtgraphs/webxml/qtgraphs-data-handling.webxml
+qtgraphs/webxml/qtgraphs-interacting-with-data.webxml
+qtgraphs/webxml/qtgraphs-known-issues.webxml
+qtgraphs/webxml/qtgraphs-migration-guide-2d.webxml
+qtgraphs/webxml/qtgraphs-migration-guide.webxml
+qtgraphs/webxml/qtgraphs-overview-2d.webxml
+qtgraphs/webxml/qtgraphs-overview-3d.webxml
+qtgraphs/webxml/qtgraphs-overview-theme.webxml
+qtgui/webxml/coordsys.webxml
+qtgui/webxml/dnd.webxml
+qtgui/webxml/gui-changes-qt6.webxml
+qtgui/webxml/paintsystem-devices.webxml
+qtgui/webxml/paintsystem-drawing.webxml
+qtgui/webxml/paintsystem-images.webxml
+qtgui/webxml/paintsystem.webxml
+qtgui/webxml/qabstracttextdocumentlayout-paintcontext.webxml
+qtgui/webxml/qabstracttextdocumentlayout-selection.webxml
+qtgui/webxml/qaccessible-state.webxml
+qtgui/webxml/qiconengine-scaledpixmapargument.webxml
+qtgui/webxml/qpageranges-range.webxml
+qtgui/webxml/qtextlayout-formatrange.webxml
+qtgui/webxml/qtextoption-tab.webxml
+qtgui/webxml/qtgui-overview.webxml
+qtgui/webxml/richtext-advanced-processing.webxml
+qtgui/webxml/richtext-common-tasks.webxml
+qtgui/webxml/richtext-cursor.webxml
+qtgui/webxml/richtext-html-subset.webxml
+qtgui/webxml/richtext-layouts.webxml
+qtgui/webxml/richtext-structure.webxml
+qtgui/webxml/richtext.webxml
+qthelp/webxml/helpsystem.webxml
+qthelp/webxml/qhelplink.webxml
+qthelp/webxml/qthelp-framework.webxml
+qthelp/webxml/qthelpproject.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-apibehavior-h.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-colorpalette-pro.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-main-cpp.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-types-h.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-utils-h.webxml
+qthttpserver/webxml/qthttpserver-logging.webxml
+qtlocation/webxml/location-places-backend.webxml
+qtlocation/webxml/location-plugin-itemsoverlay.webxml
+qtlocation/webxml/location-plugin-osm.webxml
+qtlocation/webxml/qml-location5-maps.webxml
+qtlocation/webxml/qtlocation-geoservices.webxml
+qtmultimedia/webxml/advanced-ffmpeg-configuration.webxml
+qtmultimedia/webxml/audiooverview.webxml
+qtmultimedia/webxml/cameraoverview.webxml
+qtmultimedia/webxml/multimediaoverview.webxml
+qtmultimedia/webxml/qmediatimerange-interval.webxml
+qtmultimedia/webxml/qt-add-ios-ffmpeg-libraries.webxml
+qtmultimedia/webxml/qtmultimedia-apple.webxml
+qtmultimedia/webxml/qtmultimedia-changes-qt6.webxml
+qtmultimedia/webxml/qtmultimedia-gstreamer.webxml
+qtmultimedia/webxml/qtmultimedia-modules.webxml
+qtmultimedia/webxml/qtmultimedia-wayland.webxml
+qtmultimedia/webxml/qtmultimedia-windows.webxml
+qtmultimedia/webxml/videooverview.webxml
+qtnetworkauth/webxml/oauth-http-method-alternatives.webxml
+qtnetwork/webxml/network-changes-qt6.webxml
+qtnetwork/webxml/qdtlsclientverifier-generatorparameters.webxml
+qtnetwork/webxml/qtnetwork-programming.webxml
+qtnetwork/webxml/ssl.webxml
+qtnfc/webxml/nfc-android.webxml
+qtnfc/webxml/qndeffilter-record.webxml
+qtnfc/webxml/qtnfc-changes-qt6.webxml
+qtnfc/webxml/qtnfc-features.webxml
+qtnfc/webxml/qtnfc-overview.webxml
+qtnfc/webxml/qtnfc-pcsc.webxml
+qtopengl/webxml/opengl-changes-qt6.webxml
+qtpositioning/webxml/positioning-cpp-qml.webxml
+qtpositioning/webxml/position-plugin-android.webxml
+qtpositioning/webxml/position-plugin-geoclue2.webxml
+qtpositioning/webxml/position-plugin-gypsy.webxml
+qtpositioning/webxml/position-plugin-nmea.webxml
+qtpositioning/webxml/qtpositioning-android.webxml
+qtpositioning/webxml/qtpositioning-changes-qt6.webxml
+qtpositioning/webxml/qtpositioning-ios.webxml
+qtpositioning/webxml/qtpositioning-plugins.webxml
+qtprintsupport/webxml/pdf-licensing.webxml
+qtprintsupport/webxml/printsupport-changes-qt6.webxml
+qtqml/webxml/qml-changes-qt6.webxml
+qtqml/webxml/qmldiskcache.webxml
+qtqml/webxml/qmlreference.webxml
+qtqml/webxml/qml-singleton.webxml
+qtqml/webxml/qqmlcontext-propertypair.webxml
+qtqml/webxml/qt-add-qml-plugin.webxml
+qtqml/webxml/qt-import-qml-plugins.webxml
+qtqml/webxml/qtjavascript.webxml
+qtqml/webxml/qtqml-documents-definetypes.webxml
+qtqml/webxml/qtqml-documents-networktransparency.webxml
+qtqml/webxml/qtqml-documents-scope.webxml
+qtqml/webxml/qtqml-documents-structure.webxml
+qtqml/webxml/qtqml-documents-topic.webxml
+qtqml/webxml/qtqml-javascript-dynamicobjectcreation.webxml
+qtqml/webxml/qtqml-javascript-expressions.webxml
+qtqml/webxml/qtqml-javascript-finetuning.webxml
+qtqml/webxml/qtqml-javascript-functionlist.webxml
+qtqml/webxml/qtqml-javascript-hostenvironment.webxml
+qtqml/webxml/qtqml-javascript-imports.webxml
+qtqml/webxml/qtqml-javascript-memory.webxml
+qtqml/webxml/qtqml-javascript-qmlglobalobject.webxml
+qtqml/webxml/qtqml-javascript-resources.webxml
+qtqml/webxml/qtqml-javascript-topic.webxml
+qtqml/webxml/qtqml-modules-identifiedmodules.webxml
+qtqml/webxml/qtqml-modules-legacymodules.webxml
+qtqml/webxml/qtqml-modules-qmldir.webxml
+qtqml/webxml/qtqml-modules-topic.webxml
+qtqml/webxml/qtqml-syntax-basics.webxml
+qtqml/webxml/qtqml-syntax-directoryimports.webxml
+qtqml/webxml/qtqml-syntax-imports.webxml
+qtqml/webxml/qtqml-syntax-objectattributes.webxml
+qtqml/webxml/qtqml-syntax-propertybinding.webxml
+qtqml/webxml/qtqml-syntax-signals.webxml
+qtqml/webxml/qtqml-typesystem-basictypes.webxml
+qtqml/webxml/qtqml-typesystem-namespaces.webxml
+qtqml/webxml/qtqml-typesystem-objecttypes.webxml
+qtqml/webxml/qtqml-typesystem-references.webxml
+qtqml/webxml/qtqml-typesystem-sequencetypes.webxml
+qtqml/webxml/qtqml-typesystem-topic.webxml
+qtqml/webxml/qtqml-typesystem-valuetypes.webxml
+qtqml/webxml/qt-target-qml-sources.webxml
+qtquick3d/webxml/qt3dxr-multiview.webxml
+qtquick3d/webxml/qt3dxr-pcvr.webxml
+qtquick3d/webxml/qt3dxr-quest-quick-start.webxml
+qtquick3d/webxml/qt3dxr-quick-start-guide-applevisionpro.webxml
+qtquick3d/webxml/qt3dxr-supported-openxr-extensions.webxml
+qtquick3d/webxml/qt3dxr-supported-platforms.webxml
+qtquick3d/webxml/qtaa-toc.webxml
+qtquick3d/webxml/qtquick3d-2d.webxml
+qtquick3d/webxml/qtquick3d-architecture.webxml
+qtquick3d/webxml/qtquick3d-custom.webxml
+qtquick3d/webxml/qtquick3d-lod.webxml
+qtquick3d/webxml/qtquick3d-requirements.webxml
+qtquick3d/webxml/qt-quick-3d-xr.webxml
+qtquick3d/webxml/qtxr-locomotion.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-3d-assets.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-anti-aliasing.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-blender.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-max.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-maya.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-modo.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-ibl.webxml
+qtquick3d/webxml/quick3d-asset-intro.webxml
+qtquick3d/webxml/quick3d-instancing.webxml
+qtquick3d/webxml/quick3d-lightmap.webxml
+qtquick3d/webxml/quick3d-morphing.webxml
+qtquick3d/webxml/quick3d-pbr.webxml
+qtquick3d/webxml/quick3d-shadow-mapping.webxml
+qtquick3d/webxml/quick3d-vertex-skinning.webxml
+qtquickcontrols/webxml/qtquickcontrols-basic.webxml
+qtquickcontrols/webxml/qtquickcontrols-buttons.webxml
+qtquickcontrols/webxml/qtquickcontrols-changes-qt6.webxml
+qtquickcontrols/webxml/qtquickcontrols-configuration.webxml
+qtquickcontrols/webxml/qtquickcontrols-containers.webxml
+qtquickcontrols/webxml/qtquickcontrols-customize.webxml
+qtquickcontrols/webxml/qtquickcontrols-delegates.webxml
+qtquickcontrols/webxml/qtquickcontrols-deployment.webxml
+qtquickcontrols/webxml/qtquickcontrols-environment.webxml
+qtquickcontrols/webxml/qtquickcontrols-fileselectors.webxml
+qtquickcontrols/webxml/qtquickcontrols-fluentwinui3.webxml
+qtquickcontrols/webxml/qtquickcontrols-focus.webxml
+qtquickcontrols/webxml/qtquickcontrols-fusion.webxml
+qtquickcontrols/webxml/qtquickcontrols-gettingstarted.webxml
+qtquickcontrols/webxml/qtquickcontrols-guidelines.webxml
+qtquickcontrols/webxml/qtquickcontrols-icons.webxml
+qtquickcontrols/webxml/qtquickcontrols-imagine.webxml
+qtquickcontrols/webxml/qtquickcontrols-indicators.webxml
+qtquickcontrols/webxml/qtquickcontrols-input.webxml
+qtquickcontrols/webxml/qtquickcontrols-ios.webxml
+qtquickcontrols/webxml/qtquickcontrols-macos.webxml
+qtquickcontrols/webxml/qtquickcontrols-material.webxml
+qtquickcontrols/webxml/qtquickcontrols-menus.webxml
+qtquickcontrols/webxml/qtquickcontrols-navigation.webxml
+qtquickcontrols/webxml/qtquickcontrols-popups.webxml
+qtquickcontrols/webxml/qtquickcontrols-separators.webxml
+qtquickcontrols/webxml/qtquickcontrols-styles.webxml
+qtquickcontrols/webxml/qtquickcontrols-universal.webxml
+qtquickcontrols/webxml/qtquickcontrols-windows.webxml
+qtquick/webxml/qml-advtutorial.webxml
+qtquick/webxml/qml-dynamicview-tutorial.webxml
+qtquick/webxml/qml-tutorial1.webxml
+qtquick/webxml/qml-tutorial2.webxml
+qtquick/webxml/qml-tutorial3.webxml
+qtquick/webxml/qml-tutorial.webxml
+qtquick/webxml/qquickitem-itemchangedata.webxml
+qtquick/webxml/qsggeometry-attribute.webxml
+qtquick/webxml/qsggeometry-coloredpoint2d.webxml
+qtquick/webxml/qsggeometry-point2d.webxml
+qtquick/webxml/qsggeometry-texturedpoint2d.webxml
+qtquick/webxml/qsgmaterialshader-graphicspipelinestate.webxml
+qtquick/webxml/qsgmaterialtype.webxml
+qtquick/webxml/qtquick-android-classes.webxml
+qtquick/webxml/qtquick-bestpractices.webxml
+qtquick/webxml/qtquick-codesamples.webxml
+qtquick/webxml/qtquick-convenience-topic.webxml
+qtquick/webxml/qtquick-cppextensionpoints.webxml
+qtquick/webxml/qtquick-effects-particles.webxml
+qtquick/webxml/qtquick-effects-sprites.webxml
+qtquick/webxml/qtquick-effects-topic.webxml
+qtquick/webxml/qtquick-effects-transformations.webxml
+qtquick/webxml/qtquick-how-tos.webxml
+qtquick/webxml/qtquick-input-focus.webxml
+qtquick/webxml/qtquick-input-mouseevents.webxml
+qtquick/webxml/qtquick-input-textinput.webxml
+qtquick/webxml/qtquick-input-topic.webxml
+qtquick/webxml/qtquicklayouts-overview.webxml
+qtquick/webxml/qtquicklayouts-responsive.webxml
+qtquick/webxml/qtquick-modelviewsdata-cppmodels.webxml
+qtquick/webxml/qtquick-modelviewsdata-modelview.webxml
+qtquick/webxml/qtquick-modelviewsdata-topic.webxml
+qtquick/webxml/qtquick-particles-performance.webxml
+qtquick/webxml/qtquick-positioning-anchors.webxml
+qtquick/webxml/qtquick-positioning-layouts.webxml
+qtquick/webxml/qtquick-positioning-righttoleft.webxml
+qtquick/webxml/qtquick-positioning-topic.webxml
+qtquick/webxml/qtquick-statesanimations-animations.webxml
+qtquick/webxml/qtquick-statesanimations-behaviors.webxml
+qtquick/webxml/qtquick-statesanimations-states.webxml
+qtquick/webxml/qtquick-statesanimations-topic.webxml
+qtquick/webxml/qtquick-tools-and-utilities.webxml
+qtquick/webxml/qtquickview-android-class.webxml
+qtquick/webxml/qtquick-visualcanvas-adaptations-openvg.webxml
+qtquick/webxml/qtquick-visualcanvas-adaptations-software.webxml
+qtquick/webxml/qtquick-visualcanvas-adaptations.webxml
+qtquick/webxml/qtquick-visualcanvas-coordinates.webxml
+qtquick/webxml/qtquick-visualcanvas-scenegraph-renderer.webxml
+qtquick/webxml/qtquick-visualcanvas-scenegraph.webxml
+qtquick/webxml/qtquick-visualcanvas-topic.webxml
+qtquick/webxml/qtquick-visualcanvas-visualparent.webxml
+qtquick/webxml/qtquick-visualtypes-topic.webxml
+qtquick/webxml/quick-changes-qt6.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-add-repc-merged.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-add-repc-replicas.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-add-repc-sources.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-rep-from-headers.webxml
+qtremoteobjects/webxml/qtremoteobjects-compatibility.webxml
+qtremoteobjects/webxml/qtremoteobjects-custom-transport.webxml
+qtremoteobjects/webxml/qtremoteobjects-external-schemas.webxml
+qtremoteobjects/webxml/qtremoteobjects-gettingstarted.webxml
+qtremoteobjects/webxml/qtremoteobjects-interaction.webxml
+qtremoteobjects/webxml/qtremoteobjects-node.webxml
+qtremoteobjects/webxml/qtremoteobjects-registry.webxml
+qtremoteobjects/webxml/qtremoteobjects-repc.webxml
+qtremoteobjects/webxml/qtremoteobjects-replica.webxml
+qtremoteobjects/webxml/qtremoteobjects-source.webxml
+qtremoteobjects/webxml/qtremoteobjects-troubleshooting.webxml
+qtremoteobjects/webxml/remoteobjects-changes-qt6.webxml
+qtscxml/webxml/qscxmlc.webxml
+qtscxml/webxml/qtscxml-changes-qt6.webxml
+qtscxml/webxml/qtscxml-cmake-qt-add-statecharts.webxml
+qtscxml/webxml/qtscxml-instantiating-state-machines.webxml
+qtscxml/webxml/qtscxml-overview.webxml
+qtscxml/webxml/qtscxml-scxml-compliance.webxml
+qtsensors/webxml/compatmap.webxml
+qtsensors/webxml/creating-a-sensor-plugin.webxml
+qtsensors/webxml/determining-the-default-sensor-for-a-type.webxml
+qtsensors/webxml/dynamic-sensor-backend-registration.webxml
+qtsensors/webxml/genericbackend.webxml
+qtsensors/webxml/qoutputrange.webxml
+qtsensors/webxml/qtsensors-changes-qt6.webxml
+qtsensors/webxml/qtsensors-cpp.webxml
+qtsensors/webxml/qtsensors-tutorial.webxml
+qtsensors/webxml/senorfwbackend.webxml
+qtsensors/webxml/sensors-backend-topics.webxml
+qtserialbus/webxml/qtcanbus-backends.webxml
+qtserialbus/webxml/qtmodbus-backends.webxml
+qtserialbus/webxml/qtserialbus-passthrucan-overview.webxml
+qtserialbus/webxml/qtserialbus-peakcan-overview.webxml
+qtserialbus/webxml/qtserialbus-socketcan-overview.webxml
+qtserialbus/webxml/qtserialbus-systeccan-overview.webxml
+qtserialbus/webxml/qtserialbus-tinycan-overview.webxml
+qtserialbus/webxml/qtserialbus-vectorcan-overview.webxml
+qtserialbus/webxml/qtserialbus-virtualcan-overview.webxml
+qtserialport/webxml/qtserialport-changes-qt6.webxml
+qtspatialaudio/webxml/spatialaudiooverview.webxml
+qtsql/webxml/qtsql-changes-qt6.webxml
+qtsql/webxml/sql-connecting.webxml
+qtsql/webxml/sql-driver.webxml
+qtsql/webxml/sql-forms.webxml
+qtsql/webxml/sql-model.webxml
+qtsql/webxml/sql-presenting.webxml
+qtsql/webxml/sql-programming.webxml
+qtsql/webxml/sql-sqlstatements.webxml
+qtsql/webxml/sql-types.webxml
+qtsvg/webxml/qtsvg-changes-qt6.webxml
+qtsvg/webxml/svgextensions.webxml
+qtsvg/webxml/svgrendering.webxml
+qttestlib/webxml/qtest-overview.webxml
+qttestlib/webxml/qtest-tutorial.webxml
+qttestlib/webxml/qttest-best-practices-qdoc.webxml
+qttestlib/webxml/qttestlib-tutorial6.webxml
+qttestlib/webxml/testlib-changes-qt6.webxml
+qttexttospeech/webxml/qttexttospeech-changes-qt6.webxml
+qttexttospeech/webxml/qttexttospeech-engines.webxml
+qtwebchannel/webxml/qtwebchannel-changes-qt6.webxml
+qtwebchannel/webxml/qtwebchannel-javascript.webxml
+qtwebengine/webxml/qtwebengine-changes-qt6.webxml
+qtwebengine/webxml/qtwebengine-features.webxml
+qtwebengine/webxml/qtwebengine-overview.webxml
+qtwebengine/webxml/qtwebengine-platform-notes.webxml
+qtwebengine/webxml/qtwebenginewidgets-qtwebkitportingguide.webxml
+qtwebengine/webxml/qwebenginecookiestore-filterrequest.webxml
+qtwebsockets/webxml/echoclient.webxml
+qtwebsockets/webxml/echoserver.webxml
+qtwebsockets/webxml/qtwebsockets-changes-qt6.webxml
+qtwebsockets/webxml/qtwebsockets-testing.webxml
+qtwebsockets/webxml/websockets-overview.webxml
+qtwidgets/webxml/application-windows.webxml
+qtwidgets/webxml/dialogs.webxml
+qtwidgets/webxml/focus.webxml
+qtwidgets/webxml/gallery.webxml
+qtwidgets/webxml/gestures-overview.webxml
+qtwidgets/webxml/graphicsview.webxml
+qtwidgets/webxml/layout.webxml
+qtwidgets/webxml/mainwindow.webxml
+qtwidgets/webxml/model-view-programming.webxml
+qtwidgets/webxml/modelview.webxml
+qtwidgets/webxml/qdrawutil-h.webxml
+qtwidgets/webxml/qformlayout-takerowresult.webxml
+qtwidgets/webxml/qtextedit-extraselection.webxml
+qtwidgets/webxml/qtilerules.webxml
+qtwidgets/webxml/qwidget-styling.webxml
+qtwidgets/webxml/style-reference.webxml
+qtwidgets/webxml/stylesheet-customizing.webxml
+qtwidgets/webxml/stylesheet-designer.webxml
+qtwidgets/webxml/stylesheet-reference.webxml
+qtwidgets/webxml/stylesheet-syntax.webxml
+qtwidgets/webxml/stylesheet.webxml
+qtwidgets/webxml/widget-classes.webxml
+qtwidgets/webxml/widgets-changes-qt6.webxml
+qtwidgets/webxml/widgets-tutorial.webxml
+qtxml/webxml/xml-changes-qt6.webxml
+qtxml/webxml/xml-dom-tml.webxml
+qtxml/webxml/xml-namespaces.webxml
+qtxml/webxml/xml-processing.webxml
+qtxml/webxml/xml-streaming.webxml
+qtxml/webxml/xml-tools.webxml
+
+# WebXML <group> files (lists of classes grouped by function/curated)
+[groups]
+qtcore/webxml/animation.webxml
+qtcore/webxml/events.webxml
+qtcore/webxml/io.webxml
+qtgui/webxml/painting.webxml
+qtgui/webxml/painting-3d.webxml
+qtgui/webxml/richtext-processing.webxml
+qtprintsupport/webxml/printing.webxml
+qtsql/webxml/database.webxml
+qtlocation/webxml/qml-qtlocation5-maps.webxml
+qtquick/webxml/qtquick-scenegraph-nodes.webxml
+qtquick/webxml/qtquick-scenegraph-materials.webxml
+qtnetwork/webxml/network.webxml
index 7bbe2ce1cdd3b47b73389a8bdd7cac1024139296..2b86cd7aaa7b42e93b1fabdb2b4507c92cad4a1f 100644 (file)
@@ -8,45 +8,47 @@ Basic modules
 
 These are the main modules that help you build a Widget-based UI.
 
-.. grid:: 1 3 3 3
+.. grid:: 1 3 4 4
     :gutter: 2
 
-    .. grid-item-card:: :mod:`QtCore <PySide6.QtCore>`
+    .. grid-item-card:: :mod:`Qt Core <PySide6.QtCore>`
 
-        Provides core non-GUI functionality, like signal and slots, properties,
-        base classes of item models, serialization, and more.
+        Core non-graphical classes used by other modules.
 
-    .. grid-item-card:: :mod:`QtGui <PySide6.QtGui>`
+    .. grid-item-card:: :mod:`Qt GUI <PySide6.QtGui>`
 
-        Extends QtCore with GUI functionality: Events, windows and screens,
-        OpenGL and raster-based 2D painting, as well as images.
+        Base classes for graphical user interface (GUI) components.
 
-    .. grid-item-card:: :mod:`QtWidgets <PySide6.QtWidgets>`
+    .. grid-item-card:: :mod:`Qt Widgets <PySide6.QtWidgets>`
 
-        Provides ready to use Widgets for your application, including graphical
-        elements for your UI.
+        Classes to extend Qt GUI with Python widgets.
 
 QML and Qt Quick
 ----------------
 
-Use these modules to interact with the `QML Language <https://doc.qt.io/qt-5.qmlapplications>`_,
+Use these modules to interact with the `QML Language <https://doc.qt.io/qt-6/qmlapplications.html>`_,
 from Python.
 
-.. grid:: 1 3 3 3
+.. grid:: 1 3 4 4
     :gutter: 2
 
-    .. grid-item-card:: :mod:`QtQml <PySide6.QtQml>`
+    .. grid-item-card:: :mod:`Qt Qml <PySide6.QtQml>`
 
-        The base Python API to interact with the module.
+        Classes for QML and JavaScript languages.
 
-    .. grid-item-card:: :mod:`QtQuick <PySide6.QtQuick>`
+    .. grid-item-card:: :mod:`Qt Quick <PySide6.QtQuick>`
 
-        Provides classes to embed Qt Quick in Qt applications.
+        A declarative framework for building highly dynamic applications
+        with custom UIs.
 
-    .. grid-item-card:: :mod:`QtQuickWidgets <PySide6.QtQuickWidgets>`
+    .. grid-item-card:: :mod:`Qt Quick Controls <PySide6.QtQuickControls2>`
 
-        Provides the QQuickWidget class to embed Qt Quick in widget-based
-        applications.
+        Lightweight QML types for creating performant user interfaces for
+        desktop, embedded, and mobile devices.
+
+    .. grid-item-card:: :mod:`Qt Quick Widgets <PySide6.QtQuickWidgets>`
+
+        Provides a Python widget class for displaying a Qt Quick user interface.
 
 All the modules
 ---------------
diff --git a/sources/pyside6/doc/building_from_source/index.rst b/sources/pyside6/doc/building_from_source/index.rst
new file mode 100644 (file)
index 0000000..3376516
--- /dev/null
@@ -0,0 +1,572 @@
+.. _buildingfromsource:
+
+Building from Source
+====================
+
+.. toctree::
+    :maxdepth: 1
+    :hidden:
+
+    linux.rst
+    macOS.rst
+    windows.rst
+
+.. important:: This page is focused on building |project| **from source**.
+  If you just want to install |pymodname|, you need to run: :command:`pip install pyside6`.
+
+  For more details, refer to our :ref:`getting-started` guide. Additionally, you can check the
+  :ref:`FAQ <faq>` related to the project.
+
+.. _Quick Start: gettingstarted.html
+
+General Requirements
+--------------------
+
+The following prerequisites must be installed before you build |project|.
+On **Linux** you might get them with your operating system package manager, on **macOS**
+you might get them with ``brew``, and on **Windows** you can download the installer from each
+website.
+
+* **Python**: 3.9+ `[official Python website] <https://www.python.org/downloads/>`_
+* **Qt:** 6.8+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
+* **CMake:** 3.18+ `[official CMake website] <https://cmake.org/download/>`_
+* **Git:** 2.0+. `[official Git website] <https://git-scm.com/downloads>`_
+* **libclang:** The libclang library, recommended: version 18+ for 6.8+.
+  Prebuilt versions for each OS can be `downloaded here`_.
+* Check the `Supported Platforms of Qt`_
+
+.. _downloaded here: https://download.qt.io/development_releases/prebuilt/libclang/
+.. _`Supported Platforms of Qt` : https://doc.qt.io/qt-6/supported-platforms.html
+
+Guides per platform
+-------------------
+
+You can refer to the following pages for platform specific instructions:
+
+.. grid:: 1 3 3 3
+    :gutter: 2
+
+    .. grid-item-card::
+        :img-top: ../images/windows.svg
+        :class-item: text-center
+
+        +++
+        .. button-ref:: windows
+            :color: primary
+            :outline:
+            :expand:
+
+            Windows
+
+    .. grid-item-card::
+        :img-top: ../images/macos.svg
+        :class-item: text-center
+
+        +++
+        .. button-ref:: macOS
+            :color: primary
+            :outline:
+            :expand:
+
+            macOS
+
+    .. grid-item-card::
+        :img-top: ../images/linux.svg
+        :class-item: text-center
+
+        +++
+        .. button-ref:: linux
+            :color: primary
+            :outline:
+            :expand:
+
+            Linux
+
+.. important:: |project| does not yet support WebAssembly and the mobile operating systems (Android or iOS).
+               Most Linux-based embedded OS provide PySide with their official
+               package manager (for example, `Raspbian`_ and `ArchlinuxARM`_).
+
+.. _Raspbian: https://www.raspbian.org/
+.. _ArchlinuxARM: https://archlinuxarm.org/
+
+A normal building command will look like this::
+
+    python setup.py install --qtpaths=/path/to/qtpaths \
+                            --ignore-git \
+                            --debug \
+                            --build-tests \
+                            --parallel=8 \
+                            --verbose-build \
+                            --module-subset=Core,Gui,Widgets
+
+Which will build and install the project with **debug** symbols, including the **tests**,
+using **ninja** (instead of make), and considering only the **module subset** of
+:mod:`QtCore <PySide6.QtCore>`, :mod:`QtGui <PySide6.QtGui>`, and
+:mod:`QtWidgets <PySide6.QtWidgets>`.
+
+`CMake Unity Build Mode`_ is used by default for speed-up.
+
+Other important options to consider are:
+ * ``--cmake``, to specify the path to the cmake binary,
+ * ``--reuse-build``, to rebuild only the modified files,
+ * ``--openssl=/path/to/openssl/bin``, to use a different path for OpenSSL,
+ * ``--standalone``, to copy over the Qt libraries into the final package to make it work on other
+   machines,
+ * ``--build-docs``, to enable the documentation build.
+ * ``--doc-build-online``, to build documentation using the online template (by
+   default it uses the offline)
+
+Testing the installation
+------------------------
+
+Once the installation finishes, you will be able to execute any of our examples::
+
+  python examples/widgets/widgets/tetrix/tetrix.py
+
+Running Tests
+-------------
+
+Using the ``--build-tests`` option will enable us to run all the auto tests inside the project::
+
+  python testrunner.py test > testlog.txt
+
+.. note:: On Windows, don't forget to have qtpaths in your path
+   (:command:`set PATH=C:\\\Path\\\to\\\Qt\\\6.4.0\\\msvc2019_64\\\bin;%PATH%`)
+
+You can also run a specific test (for example ``qpainter_test``) by running::
+
+    ctest -R qpainter_test --verbose
+
+.. _cross_compilation:
+
+Cross Compilation
+-----------------
+
+Starting from 6.3, it is possible to cross-compile Shiboken (module), and
+PySide. This functionality is still in Technical Preview, which means it could
+change in the future releases.
+
+.. important:: The only supported configuration is using a host Linux
+   machine to cross-compile to a Linux target platform.
+
+Cross compiling software is a valid use case that many projects rely on,
+however, it is a complicated process that might fail due to many reasons.
+
+Before starting with the process, it is important to understand the details of
+the build system, and the goal of cross compilation.
+
+In the build process, a ``Host`` is the computer you are currently using to
+compile, and a ``Target`` is your embedded device that you are compiling for.
+
+Qt for Python is being built using setuptools, and relies on a ``setup.py`` file
+that is called recursively to build Shiboken (module),
+Shiboken (generator), and PySide. As the generator is creating
+the wrappers for the bindings, it's not cross compiled
+for the target.
+Only the Shiboken (module) and PySide are cross compiled.
+
+The building process requires a Qt installation, and a Python interpreter
+on both the host, and the target. The used Qt versions on both platforms
+should have the same minor version. That is, Qt 6.3 (host)
+cannot be used with a Qt 6.2 (target), or the other way around.
+
+.. note:: It is recommended to use the same version of Qt on both host and target to avoid possible
+  unconventional errors.
+
+Prerequisites
+~~~~~~~~~~~~~
+
+First and foremost, you need to have access to the target device because you
+need to copy several system files (sysroot). We recommend a Linux OS that has
+the latest Qt versions, like `Manjaro ARM`_ or `Archlinux ARM`_.
+
+* (target) Install Qt 6.3+ on the system using the package manager.
+* (host) Install Qt 6.3+ on the system using the package manager or Qt
+  Installer.
+* (target, host) Install the library and development packages that provide
+  C++ headers, linkers, libraries, and compilers.
+* (target, host) Install Python interpreter v3.7 or later
+* (target, host) Install CMake 3.17+
+
+After installing these prerequisites, copy the ``target`` sysroot to your
+``host`` computer. This process is tricky, because copying system files from
+another computer might cause problems with the symbolic links. Here you
+have two options to achieve that.
+
+Option A: Copying the files
+***************************
+
+Create a directory to copy the sysroot of your target device,
+for example ``rpi-sysroot``, and perform the copy on your host computer:
+
+.. code-block:: bash
+
+    rsync -vR --progress -rl --delete-after --safe-links \
+        USERNAME@TARGET_IP:/{lib,usr,opt/vc/lib} rpi-sysroot/
+
+Ensure to replace ``USERNAME`` and ``TARGET_IP`` with your system appropriate
+values.
+
+Option B: Packaging the file system
+***********************************
+
+Create a package for your sysroot in your target:
+
+.. code-block:: bash
+
+    tar cfJ ~/sysroot.tar.xz /lib /usr /opt/vc/lib
+
+Copy the package from the target to your host:
+
+.. code-block:: bash
+
+    rsync -vR --progress USERNAME@TARGET_IP:sysroot.tar.xz .
+
+Once you have the tar file, unpack it inside a ``rpi-sysroot`` directory.
+
+Fix Symlinks
+************
+
+It is recommended to run the following script to fix
+most of the issues you would find with symbolic links:
+
+.. code-block:: python
+
+    import sys
+    from pathlib import Path
+    import os
+
+    # Take a sysroot directory and turn all the absolute symlinks and turn them into
+    # relative ones such that the sysroot is usable within another system.
+
+    if len(sys.argv) != 2:
+        print(f"Usage is {sys.argv[0]} <sysroot-directory>")
+        sys.exit(-1)
+
+    topdir = Path(sys.argv[1]).absolute()
+
+    def handlelink(filep, subdir):
+        link = filep.readlink()
+        if str(link)[0] != "/":
+            return
+        if link.startswith(topdir):
+            return
+        relpath = os.path.relpath((topdir / link).resolve(), subdir)
+        os.unlink(filep)
+        os.symlink(relpath, filep)
+
+    for f in topdir.glob("**/*"):
+        if f.is_file() and f.is_symlink():
+            handlelink(f, f.parent)
+
+Setting up the toolchain
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+To perform the cross compilation, you need a special set of compilers,
+libraries, and headers, which runs on the host architecture, but generates
+(binaries/executables) for a target architecture.
+For example, from x86_64 to aarch64.
+
+It is recommended to use the official 10.2 `ARM Developer cross compilers`_,
+which you can find on their official website. For this tutorial, we choose
+``aarch64`` target architecture and we will assume that you downloaded the
+`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`_ file,
+and unpacked it.
+
+With those compilers, now you need a CMake toolchain file. This is
+a configuration file to set the compilers and sysroot information, together
+with extra options like compilation flags, and other details. You can use the
+following file as an example, but keep in mind they might vary:
+
+.. code-block:: cmake
+
+    # toolchain-aarch64.cmake
+    cmake_minimum_required(VERSION 3.18)
+    include_guard(GLOBAL)
+
+    set(CMAKE_SYSTEM_NAME Linux)
+    set(CMAKE_SYSTEM_PROCESSOR aarch64)
+
+    set(TARGET_SYSROOT /path/to/your/target/sysroot)
+    set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
+
+    set(CMAKE_SYSROOT ${TARGET_SYSROOT})
+
+    set(ENV{PKG_CONFIG_PATH} "")
+    set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
+    set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
+
+    set(CMAKE_C_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-gcc)
+    set(CMAKE_CXX_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-g++)
+
+    set(QT_COMPILER_FLAGS "-march=armv8-a")
+    set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
+    set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")
+
+    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+
+    include(CMakeInitializeConfigs)
+
+    function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING)
+      if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS")
+        set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}")
+
+        foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
+          if (DEFINED QT_COMPILER_FLAGS_${config})
+            set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}")
+          endif()
+        endforeach()
+      endif()
+
+      if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS")
+        foreach (config SHARED MODULE EXE)
+          set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}")
+        endforeach()
+      endif()
+
+      _cmake_initialize_per_config_variable(${ARGV})
+    endfunction()
+
+You need to adjust the paths in these two lines::
+
+    set(TARGET_SYSROOT /path/to/your/target/sysroot)
+    set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
+
+and replace them with the sysroot directory (the one we called ``rpi-sysroot``),
+and the compilers (the ``gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin`` directory).
+
+
+Cross compiling PySide
+~~~~~~~~~~~~~~~~~~~~~~
+
+After you have installed the prerequisites and copied the necessary files, you
+should have the following:
+
+* The compilers to cross compile (``gcc-argm-10.2-...``),
+* The target sysroot (``rpi-sysroot``),
+* The toolchain cmake file (``toolchain-aarch64.cmake``),
+* The ``pyside-setup`` repository,
+
+An example of the ``setup.py`` invocation might look like the following:
+
+.. code-block:: bash
+
+    python setup.py bdist_wheel \
+        --parallel=8 --ignore-git --reuse-build --standalone --limited-api=yes \
+        --cmake-toolchain-file=/opt/toolchain-aarch64.cmake \
+        --qt-host-path=/opt/Qt/6.3.0/gcc_64 \
+        --plat-name=linux_aarch64
+
+Depending on the target platform, you could use ``linux_armv7``,
+``linux_aarch64``, etc.
+
+For embedded platforms, which typically do not have Qt and its tools fully
+built, the option ``--no-qt-tools`` can be used to prevent the bundling of the
+tools.
+
+If the process succeeds, you will find the target wheels in your ``dist/``
+directory, for example:
+
+.. code-block:: bash
+
+    PySide6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
+    shiboken6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
+
+
+Troubleshooting
+***************
+
+* If the auto-detection mechanism fails to find the Python or Qt installations
+  you have in your target device, you can use two additional options::
+
+      --python-target-path=...
+
+  and::
+
+      --qt-target-path=...
+
+* In case the automatic build of the host Shiboken (generator) fails,
+  you can specify the custom path using::
+
+      --shiboken-host-path=...
+
+.. _`Manjaro ARM`: https://manjaro.org/products/download/arm
+.. _`Archlinux ARM`: https://archlinuxarm.org
+.. _`ARM Developer Cross Compilers`: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads
+.. _`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`: https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz
+
+.. _building_documentation:
+
+Building the documentation
+--------------------------
+
+Starting from 6.3, the documentation is not being built by default.
+When using the second option described in this section, make sure to use the
+``--build-docs`` option.
+
+Install the specific documentation requirements in your Python virtual
+environment::
+
+    pip install -r requirements-doc.txt
+
+You can find the ``requirements-doc.txt`` file in the root of the repository.
+
+Starting from 5.15, there are two options to build the documentation:
+
+1. Building the base documentation (no API)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The process of parsing Qt headers to generate the PySide API documentation can take several
+minutes, this means that modifying a specific section of the documentation we currently have, might
+become a hard task. You may only care about the base documentation, which comprises all the
+documentation except for the API documentation.
+
+To generate this, execute the following command::
+
+    python setup.py build_base_docs
+
+This will generate an ``html/`` directory with the following structure::
+
+    html
+    â””── pyside6
+        â”œâ”€â”€ index.html
+        â”œâ”€â”€ ...
+        â””── shiboken6
+            â”œâ”€â”€ index.html
+            â””── ...
+
+so you can open the main page ``html/pyside6/index.html`` on your browser to check the generated
+files.
+
+This is useful when updating the general sections of the documentation, adding tutorials,
+modifying the build instructions, and more.
+
+.. note:: In case you are interested in only generating the Example Gallery,
+          you would need to run ``python tools/example_gallery/main.py`` to
+          generate the examples ``documentation`` for the gallery. This will
+          also be used internally by the ``build_base_docs`` target
+
+
+2. Building the documentation (Base + API)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The documentation is being generated using **qdoc** to get the API information, and also **sphinx**
+for the local Python related notes.
+
+The system requires the ``libxml2`` and ``libxslt`` libraries.
+
+After installing ``graphviz`` (included in the ``requirements-doc.txt`` file),
+the ``dot`` command needs to be in PATH, otherwise,
+the process will fail. Installing ``graphviz`` system-wide is also an option.
+
+Since the process relies on a Qt installation, you need to specify where the
+``qtbase`` directory of a Qt source tree is located by passing it to
+the command line option ``--qt-src-dir``.
+
+Once the common ``setup.py`` build process finishes (remember to use
+``--build-docs`` to enable the documentation build, and ``--doc-build-online``
+to get the HTML files), you can go to the generated
+``build/<your_env_name>/build/pyside6`` directory, and run::
+
+    ninja apidoc
+
+You can add ``-j X``, to perform the build process in parallel with
+X processes.
+
+.. note:: The :command:`apidoc` make target builds offline documentation in ``QCH``
+   (Qt Compressed Help) format by default. You can switch to building for the
+   online use with the ``--doc-build-online`` configure option.
+
+The target executes several steps:
+
+#. The ``qdoc`` tool is run over the Qt source code to produce documentation in WebXML format.
+#. ``shiboken6`` is run to extract the functions for which bindings exist from WebXML and convert it into RST.
+#. ``sphinx`` is run to produce the documentation in HTML format.
+
+Re-running the command will not execute step 1 unless the file
+``qdoc-output/webxml/qtcore-index.webxml`` is removed from the build tree.
+Similarly, step 2 will not be executed unless the file ``base/PySide6/QtCore/index.rst``
+is removed.
+
+Finally, you will get a ``html`` directory containing all the generated documentation. The offline
+help files, ``PySide.qch`` and ``Shiboken.qch``, can be moved to any directory of your choice. You
+can find ``Shiboken.qch`` in the build directory, ``build/<your_env_name>/build/shiboken6/doc/html``.
+
+If you want to temporarily change a ``.rst`` file to examine the impact on
+formatting, you can re-run ``sphinx`` in the ``doc`` directory::
+
+    sphinx-build base html
+
+Viewing offline documentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The offline documentation (QCH) can be viewed using the *Qt Creator* IDE or
+*Qt Assistant*, which is a standalone application for viewing QCH files.
+
+To view the QCH using *Qt Creator*, following the instructions outlined in
+`Using Qt Creator Help Mode <https://doc.qt.io/qtcreator/creator-how-to-add-external-documentation.html>`_.
+If you chose to use *Qt Assistant* instead, use the following command to register
+the QCH file before launching *Qt Assistant*::
+
+    assistant -register PySide.qch
+
+Troubleshooting documentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The documentation uses intersphinx to link from the PySide to the
+Shiboken documentation. This can fail if
+
+* the default ``QCH`` format is used; in which case the required ``objects.inv``
+  files are not generated. Use ``--doc-build-online``.
+* base and full doc builds are mixed, resulting in wrong values for the
+  intersphinx location in the CMake files. Re-run ``cmake`` to fix this.
+
+Using the internal tools
+------------------------
+
+A set of tools can be found under the ``tools/`` directory inside the ``pyside-setup`` repository.
+
+* ``checklibs.py``: Script to analyze dynamic library dependencies of Mach-O binaries.
+  To use this utility, just run::
+
+    python checklibs.py /path/to/some.app/Contents/MacOS/Some
+
+  This script was fetched from this repository_.
+
+* ``create_changelog.py``: Script used to create the CHANGELOG that you can find in the ``dist/``
+  directory. Usage::
+
+    python create_changelog.py -r 6.0.1 -v v6.0.0..6.0 -t bug-fix
+
+* ``debug_windows.py``: This script can be used to find out why PySide modules
+  fail to load with various DLL errors like Missing DLL or Missing symbol in DLL.
+
+  You can think of it as a Windows version of :command:`ldd` / ``LD_DEBUG``.
+
+  Underneath, it uses the :command:`cdb.exe` command line debugger and the :command:`gflags.exe`
+  tool, which are installed with the latest Windows Kit.
+
+  The aim is to help developers debug issues that they may encounter using the PySide imports on
+  Windows. The user should then provide the generated log file.
+
+  Incidentally it can also be used for any Windows executables, not just Python.
+  To use it just run::
+
+    python debug_windows.py
+
+* ``missing_bindings.py``: This script is used to compare the state of PySide and PyQt
+  regarding available modules and classses. This content is displayed in our `wiki page`_,
+  and can be used as follows::
+
+    python missing_bindings.py --qt-version 6.0.1 -w all
+
+.. note:: The script relies on BeautifulSoup to parse the content and generate a list of the
+   missing bindings.
+
+.. _repository: https://github.com/liyanage/macosx-shell-scripts/
+.. _`wiki page`: https://wiki.qt.io/Qt_for_Python_Missing_Bindings
+.. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/
+.. _`CMake Unity Build Mode` : https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.html
diff --git a/sources/pyside6/doc/building_from_source/linux.rst b/sources/pyside6/doc/building_from_source/linux.rst
new file mode 100644 (file)
index 0000000..8e64720
--- /dev/null
@@ -0,0 +1,149 @@
+Linux
+=====
+
+Requirements
+------------
+
+* GCC
+* ``sphinx`` package for the documentation (optional).
+* Depending on your linux distribution, the following dependencies might also be required:
+
+  * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
+* Check the platform dependencies of `Qt for Linux/X11`_.
+
+Building from source
+--------------------
+
+Creating a virtual environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
+which arbitrary modules can be installed and which can be removed after use::
+
+    python -m venv testenv
+    source testenv/bin/activate
+
+will create and use a new virtual environment, which is indicated by the command prompt changing.
+
+Alternatively, you can use the `uv`_ tool, which is popular in the Python community for Python
+project management. The following command creates a virtual environment using `uv`_::
+
+    uv venv --python <python-version> testenv
+    source testenv/bin/activate
+
+.. note:: Since the Qt for Python project still uses `setup.py` and not `pyproject.toml`, currently
+          `uv` can only be used as a replacement for `pyenv` for building Qt for Python. If you
+          have already the `.python-version` file (used by .pyenv) in the project, make sure to
+          change the version to the `uv`_ Python you want to use.
+
+Setting up CLANG
+~~~~~~~~~~~~~~~~
+
+If you don't have libclang already in your system, you can download from the Qt servers::
+
+    wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.5-based-linux-Rhel8.6-gcc10.3-x86_64.7z
+
+Extract the files, and leave it in any desired path, and set the environment
+variable required::
+
+    7z x libclang-release_18.1.5-based-linux-Rhel8.6-gcc10.3-x86_64.7z
+    export LLVM_INSTALL_DIR=$PWD/libclang
+
+Getting the source
+~~~~~~~~~~~~~~~~~~
+
+Cloning the official repository can be done by::
+
+    git clone https://code.qt.io/pyside/pyside-setup
+
+Checking out the version that we want to build, for example 6.8::
+
+    cd pyside-setup && git checkout 6.8
+
+Install the general dependencies::
+
+    pip install -r requirements.txt
+
+For building the documentation::
+
+    pip install -r requirements-doc.txt
+
+.. note:: Keep in mind you need to use the same version as your Qt installation.
+          Additionally, :command:`git checkout -b 6.8 --track origin/6.8` could be a better option
+          in case you want to work on it.
+
+.. note:: With `uv`_, use `uv pip install ...`
+
+Building and Installing (setuptools)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``setuptools`` approach uses the ``setup.py`` file to execute the build step and further
+uses `create_wheels.py`_ to create the wheels. Once the wheels are created, you can install the
+wheels using the `pip` command.
+
+Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
+for example, :command:`/opt/Qt/6.8.0/gcc_64/bin/qtpaths`.
+
+Build can take a few minutes, so it is recommended to use more than one CPU core::
+
+    python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+With `uv`_, the build command becomes::
+
+    uv run setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+To create the wheels, just run::
+
+    python create_wheels.py --build-dir=/directory/where/pyside/is/built --no-examples
+
+On successful completion, the wheels will be created in the `dist` directory.
+
+.. note:: The `build-dir` typically looks like `build/<your_python_environment_name>`. The
+          requirement is that this `build-dir` should contain the `packages_for_wheel` directory.
+          If the `python setup.py` build command was successful, this directory should be present.
+
+Finally, to install the wheels, use the following command::
+
+    pip install dist/*.whl
+
+Building and Installing (cmake)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``setuptools`` approach includes internal ``CMake`` calls when
+building and installing the project, but a CMake-only approach is only
+recommended for packaging the project for distribution builds.
+
+Assuming that Qt is in PATH, for example, the configure step can be done with::
+
+    cmake -B /path/to/the/build/directory \
+          -S /path/to/the/pyside-setup \
+          -DCMAKE_INSTALL_PREFIX=/where/to/install \
+          -DPython_EXECUTABLE=/path/to/interpreter
+
+.. note:: You can add `-DFORCE_LIMITED_API=yes` in case you want to have a
+   build which will be compatible with Python 3.8+.
+
+and then for building::
+
+    cmake --build /path/to/the/build/directory --parallel X
+
+where `X` is the number of processes you want to use.
+Finally, the install step can be done with::
+
+    cmake --install /path/to/the/build/directory
+
+.. note:: You can build only pyside6 or only shiboken6 by using
+   the different source directories with the option `-S`.
+
+
+Test installation
+~~~~~~~~~~~~~~~~~
+
+You can execute one of the examples to verify the process is properly working.
+Remember to properly set the environment variables for Qt and PySide::
+
+    python examples/widgets/widgets/tetrix/tetrix.py
+
+.. _`Qt for Linux/X11`: https://doc.qt.io/qt-6/linux.html
+.. _`uv`: https://docs.astral.sh/uv/
+.. _`create_wheels.py`: https://code.qt.io/cgit/pyside/pyside-setup.git/tree/create_wheels.py
diff --git a/sources/pyside6/doc/building_from_source/macOS.rst b/sources/pyside6/doc/building_from_source/macOS.rst
new file mode 100644 (file)
index 0000000..dca804c
--- /dev/null
@@ -0,0 +1,121 @@
+macOS
+=====
+
+Requirements
+------------
+
+* `Xcode`_ 8.2 (macOS 10.11), 8.3.3 (macOS 10.12), 9 (macOS 10.13), 10.1 (macOS 10.14)
+* ``sphinx`` package for the documentation (optional).
+* Depending on your OS, the following dependencies might also be required:
+
+  * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
+
+* Check the platform dependencies of `Qt for macOS`_.
+
+.. _Xcode: https://developer.apple.com/xcode/
+.. _`Qt for macOS`: https://doc.qt.io/qt-6/macos.html
+
+Building from source
+--------------------
+
+Creating a virtual environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
+which arbitrary modules can be installed and which can be removed after use::
+
+    python -m venv testenv  # your interpreter could be called 'python3'
+    source testenv/bin/activate
+
+will create and use a new virtual environment, which is indicated by the command prompt changing.
+
+Alternatively, you can use the `uv`_ tool, which is popular in the Python community for Python
+project management. The following command creates a virtual environment using `uv`_::
+
+    uv venv --python <python-version> testenv
+    source testenv/bin/activate
+
+.. note:: Since the Qt for Python project still uses `setup.py` and not `pyproject.toml`, currently
+          `uv` can only be used as a replacement for `pyenv` for building Qt for Python. If you
+          have already the `.python-version` file (used by `pyenv`) in the project, make sure to
+          change the version to the `uv`_ Python you want to use.
+
+Setting up CLANG
+~~~~~~~~~~~~~~~~
+
+If you don't have `libclang` already in your system, you can download from the Qt servers::
+
+    wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.5-based-macos-universal.7z
+
+Extract the files, and leave it in any desired path, and set the environment
+variable required::
+
+    7z x libclang-release_18.1.5-based-macos-universal.7z
+    export LLVM_INSTALL_DIR=$PWD/libclang
+
+Getting PySide
+~~~~~~~~~~~~~~
+
+Cloning the official repository can be done by::
+
+    git clone https://code.qt.io/pyside/pyside-setup
+
+Checking out the version that we want to build, for example, 6.8::
+
+    cd pyside-setup && git checkout 6.8
+
+Install the general dependencies::
+
+    pip install -r requirements.txt
+
+For building the documentation::
+
+    pip install -r requirements-doc.txt
+
+.. note:: Keep in mind you need to use the same version as your Qt installation
+
+.. note:: With `uv`_, use `uv pip install ...
+
+Building PySide
+~~~~~~~~~~~~~~~
+
+Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
+for example, ``/opt/Qt/6.8.0/gcc_64/bin/qtpaths``.
+
+Build can take a few minutes, so it is recommended to use more than one CPU core::
+
+    python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+With `uv`_, this command becomes::
+
+    uv run setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+
+Installing PySide
+~~~~~~~~~~~~~~~~~
+
+First, create the wheels using the `create_wheels.py`_ script::
+
+    python create_wheels.py --build-dir=/directory/where/pyside/is/built --no-examples
+
+On successful completion, the wheels will be created in the `dist` directory.
+
+.. note:: The `build-dir` typically looks like `build/<your_python_environment_name>`. The
+          requirement is that this `build-dir` should contain the `packages_for_wheel` directory.
+          If the `python setup.py` build command was successful, this directory should be present.
+
+Finally, to install the wheels, use the following command::
+
+    pip install dist/*.whl
+
+Test installation
+~~~~~~~~~~~~~~~~~
+
+You can execute one of the examples to verify the process is properly working.
+Remember to properly set the environment variables for Qt and PySide::
+
+    python examples/widgets/widgets/tetrix/tetrix.py
+
+.. _`uv`: https://docs.astral.sh/uv/
+.. _`create_wheels.py`: https://code.qt.io/cgit/pyside/pyside-setup.git/tree/create_wheels.py
+
diff --git a/sources/pyside6/doc/building_from_source/windows.rst b/sources/pyside6/doc/building_from_source/windows.rst
new file mode 100644 (file)
index 0000000..d2510a1
--- /dev/null
@@ -0,0 +1,145 @@
+Windows
+=======
+
+The Qt library has to be built with the same version of MSVC as Python and PySide, this can be
+selected when using the online installer.
+
+Requirements
+------------
+
+* `MSVC2022`_ for Python 3 on Windows,
+* `OpenSSL`_ (optional for SSL support, Qt must have been configured using the same SSL library).
+* ``sphinx`` package for the documentation (optional).
+* Check the platform dependencies of `Qt for Windows`_.
+
+.. note:: The Python provided by the Microsoft Store is not compatible with PySide. Please
+    use https://www.python.org/downloads/ to get a Python Interpreter.
+
+.. _MSVC2022: https://visualstudio.microsoft.com/downloads/
+.. _OpenSSL: https://sourceforge.net/projects/openssl/
+.. _`Qt for Windows`: https://doc.qt.io/qt-6/windows.html
+
+Building from source on Windows 10
+----------------------------------
+
+Creating a virtual environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``venv`` module allows you to create a local, user-writable copy of a python environment into
+which arbitrary modules can be installed and which can be removed after use::
+
+    python -m venv testenv
+    call testenv\Scripts\activate
+
+will create and use a new virtual environment, which is indicated by the command prompt changing.
+
+Alternatively, you can use the `uv`_ tool, which is popular in the Python community for Python
+project management. The following command creates a virtual environment using `uv`_::
+
+    uv venv --python <python-version> testenv
+    call testenv\Scripts\activate
+
+.. note:: Since the Qt for Python project still uses `setup.py` and not `pyproject.toml`, currently
+          `uv` can only be used as a replacement for `pyenv` for building Qt for Python. If you
+          have already the `.python_version` file (used by .pyenv) in the project, make sure to
+          change the version to the `uv`_ Python you want to use.
+
+Setting up CLANG
+~~~~~~~~~~~~~~~~
+
+libclang can be downloaded from the
+`Qt servers <https://download.qt.io/development_releases/prebuilt/libclang>`_.
+for example, ``libclang-release_18.1.5-based-windows-vs2019_64.7z``.
+
+Note that from version 12 onwards, the prebuilt Windows binaries from
+`LLVM <https://www.llvm.org>`_ no longer contain CMake configuration files; so
+they can no longer be used.
+
+Extract the files, and leave it on any desired path, for example, ``c:``,
+and set the environment variable required::
+
+    set LLVM_INSTALL_DIR=c:\libclang
+    set PATH=C:\libclang\bin;%PATH%
+
+Getting PySide
+~~~~~~~~~~~~~~
+
+Cloning the official repository can be done by::
+
+    git clone https://code.qt.io/pyside/pyside-setup
+
+Checking out the version that we want to build, for example, 6.8::
+
+    cd pyside-setup && git checkout 6.8
+
+Install the general dependencies::
+
+    pip install -r requirements.txt
+
+For building the documentation::
+
+    pip install -r requirements-doc.txt
+
+.. note:: Keep in mind you need to use the same version as your Qt installation
+
+.. note:: With `uv`_, use `uv pip install ...`
+
+Building PySide
+~~~~~~~~~~~~~~~
+
+Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
+for example, ``C:\Qt\6.8.0\msvc2019_64\bin\qtpaths.exe``.
+
+Build can take a few minutes, so it is recommended to use more than one CPU core::
+
+    python setup.py build --qtpaths=c:\path\to\qtpaths.exe --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
+
+With `uv`_, this command becomes::
+
+    uv run setup.py build --qtpaths=c:\path\to\qtpaths.exe --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
+
+
+.. _creating_windows_debug_builds:
+
+Creating Debug Builds
+~~~~~~~~~~~~~~~~~~~~~
+
+* Choose *Custom Installation* when installing Python and tick the options for
+  debug binaries and libraries
+
+* Use ``venv`` to create a virtual environment and pass the debug binary::
+
+   python_d.exe -m venv testenv_d
+
+* Use ``python_d.exe`` to invoke ``setup.py``
+
+.. note:: Make sure you add the ``--debug`` option to the ``python setup.py install`` to produce a debug build
+
+
+Installing PySide
+~~~~~~~~~~~~~~~~~
+
+First, create the wheels using the `create_wheels.py`_ script::
+
+    python create_wheels.py --build-dir=C:\directory\where\pyside\is\built --no-examples
+
+On successful completion, the wheels will be created in the `dist` directory.
+
+.. note:: The `build-dir` typically looks like `build\<your_python_environment_name>`. The
+          requirement is that this `build-dir` should contain the `packages_for_wheel` directory.
+          If the `python setup.py` build command was successful, this directory should be present.
+
+Finally, to install the wheels, use the following command::
+
+    pip install dist\*.whl
+
+
+Test installation
+~~~~~~~~~~~~~~~~~
+
+You can execute one of the examples to verify the process is properly working.
+Remember to properly set the environment variables for Qt and PySide::
+
+    python examples\widgets\widgets\tetrix\tetrix.py
+
+.. _`uv`: https://docs.astral.sh/uv/
index e74419d6acfcdfe1c2e8b86b1813e2bca756378d..d3062629d45323ee605e41fb044eeba804797f2e 100644 (file)
@@ -42,6 +42,9 @@ qtpip - a commercial wheel installer
 `qtpip`_ is a wrapper around `pip`_ (the package installer for Python) that provides an
 integration with the detection of commercial licenses.
 
+It requires that a Qt license is present. On a fresh install,
+this can be done by launching the `Qt Maintenance Tool`_ first.
+
 To set up the tool, we recommend creating a virtual environment for your
 project, and then installing the tool like any other module:
 
@@ -109,7 +112,7 @@ simplify the installation step::
 Complementary to the wheels, you will be able to download the sources
 as well.
 
-.. note:: Wheels installed this way will be detectable by `*Qt Creator*`_, which
+.. note:: Wheels installed this way will be detectable by `Qt Creator`_, which
    will offer you to install them for your current Python interpreter.
 
 Using account.qt.io
@@ -151,7 +154,7 @@ Operating System (macOS, Windows, or Linux). To learn more about what the
 packages contain, please check the :ref:`package_details` page.
 
 Once you download the packages, you are encouraged to create a Python virtual
-environment to install them - check the ref:`quick-start` page for how to do
+environment to install them - check the :ref:`getting-started` page for how to do
 it.
 With your activated environment on a terminal, run the following command (for
 macOS/Linux)::
@@ -247,4 +250,3 @@ packages will likely happen.
 .. _`Qt Creator`: https://www.qt.io/product/development-tools
 .. _`qtpip`: https://pypi.org/project/qtpip/
 .. _`pip`: https://pypi.org/project/pip/
-
index e195d227fe8822180bc8aa3f587179df6c2b1131..5af91019be9d3ab6c22ef6cd9e234600b342c17b 100644 (file)
@@ -13,6 +13,8 @@
 
 import sys
 import os
+from docutils import nodes
+from docutils.parsers.rst import roles
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
@@ -31,7 +33,7 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.ifconfig',
     'sphinx.ext.graphviz', 'inheritance_diagram', 'pysideinclude',
     'sphinx.ext.viewcode',
     'sphinx_design', 'sphinx_copybutton', 'myst_parser', 'sphinx_tags',
-    'sphinx_toolbox.decorators']
+    'sphinx_toolbox.decorators', 'sphinx_reredirects']
 
 myst_enable_extensions = [
     "amsmath",
@@ -51,8 +53,14 @@ myst_heading_anchors = 6
 
 output_format='@DOC_OUTPUT_FORMAT@'
 
+def deco_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+    # Create a reference node
+    node = nodes.reference(rawtext, text, refuri='#', **options)
+    return [node], []
+
 def setup(app):
-    app.add_config_value('output_format','qthelp','env')
+    app.add_config_value('output_format', 'qthelp', 'env')
+    roles.register_local_role('deco', deco_role)
 
 rst_epilog = """
 .. |project| replace:: Qt for Python
@@ -150,12 +158,16 @@ html_theme_options = {
         "color-admonition-title--important": "#2cde85",
         "color-admonition-title-background--important": "#474b53",
         "font-stack": "'Titillium Web', sans-serif",
+        "color-background": "rgba(0, 0, 0, 0.9)",
+        "color-background-font": "#FFFFFF",
     },
     "light_css_variables": {
         "color-brand-primary": "#27138b",
         "color-brand-content": "#27138b",
         "color-admonition-title--important": "#27138b",
         "font-stack": "'Titillium Web', sans-serif",
+        "color-background": "rgba(255, 255, 255, 0.9)",
+        "color-background-font": "#000000",
     },
 }
 
@@ -176,7 +188,7 @@ html_logo = "@CMAKE_CURRENT_SOURCE_DIR@/_static/qtforpython.png"
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-html_favicon = "_static/qtforpython.ico"
+html_favicon = "@CMAKE_CURRENT_SOURCE_DIR@/_static/qtforpython.ico"
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
@@ -242,3 +254,21 @@ tags_create_badges = True
 tags_badge_colors = {
     "Android": "info",
 }
+
+# link redirection in sphinx
+redirects = {
+    "gettingstarted/porting_from2.html": "../faq/porting_from2.html",
+    "gettingstarted/package_details.html": "../package_details.html",
+    "gettingstarted/index.html": "../building_from_source/index.html",
+    "gettingstarted/linux.html": "../building_from_source/linux.html",
+    "gettingstarted/windows.html": "../building_from_source/windows.html",
+    "gettingstarted/macOS.html": "../building_from_source/macOS.html",
+    "quickstart.html": "gettingstarted.html"
+}
+
+# ignore relative paths when linkcheck is running
+# sphinx-build -b linkcheck -j auto -n -c <folder_of_conf.py> <folder_of_rst> <output_dir>
+linkcheck_ignore = [
+    r'^\.\./',  # Ignore all paths starting with ../
+    r'^\./',    # Ignore all paths starting with ./
+]
index dda1d8b0db4e21dbcc774c9fafae0e13d76b91ff..506f19aba9fddf6da12f9b4a483a1949ac3a9f89 100644 (file)
@@ -197,7 +197,7 @@ With the above features selected, this reads:
     self.table.horizontal_header().section_resize_mode = QHeaderView.Stretch
 
 Additionally, properties can also be declared directly in Shiboken for
-non Qt-libraries, see :ref:`property-declare`.
+non Qt-libraries, see :ref:`property-declare <shiboken6:property-declare>`.
 
 More about features
 ~~~~~~~~~~~~~~~~~~~
@@ -211,17 +211,17 @@ Tools
 
 * ``pyside6-rcc``: Qt Resource Compiler. This is a command line tool
   that compiles ``.qrc`` files containing binary data, for example images,
-  into executable Python code (see :ref:`using_qrc_files`).
+  into executable Python code (see :ref:`tutorial_qrcfiles`).
 * ``pyside6-uic``: Qt User Interface Compiler. This is a command line tool
   that compiles ``.ui`` files containing designs of Qt Widget-based forms
-  into executable Python code (see :ref:`using_ui_files`).
+  into executable Python code (see :ref:`tutorial_uifiles`).
 * ``pyside6-assistant``: Qt Help Viewer. This is a graphical tool that can
   be used to view Qt documentation from Qt Compressed Help files (``.qhc``).
   Currently, only the binary without documentation sets is shipped to reduce the
   wheel size. For building the documentation, see :ref:`building_documentation`.
 * ``pyside6-designer``: Qt User Interface Designer. This is a graphical tool
   to create designs of Qt Widget-based forms and use custom widgets
-  (see :ref:`using_ui_files`, :ref:`designer_custom_widgets`).
+  (see :ref:`tutorial_uifiles`, :ref:`designer_custom_widgets`).
 
 
 .. _NewEnumSystem:
@@ -395,8 +395,8 @@ Calendar. More about permission API can be read in this `Blog post`_.
 When a PySide6 application that uses the permission API is run in interpreted mode, i.e.,
 ``python <main_file>.py``, the code implementing the permission API *will not work*. The only way
 to make your PySide6 application using permission API work is to bundle the application. For Android,
-this means using the `pyside6-android-deploy`_ tool and for macOS, this means using the
-`pyside6-deploy`_ tool.
+this means using the :ref:`pyside6-android-deploy` tool and for macOS, this means using the
+:ref:`pyside6-deploy` tool.
 
 When running in interpreted mode, you can skip over the permission check/request using the following
 *if* condition
@@ -413,7 +413,7 @@ check if the application is run as a standalone application or run in interprete
 Android
 ~~~~~~~~
 
-For Android, `pyside6-android-deploy`_ takes care of identifying the necessary permissions needed by
+For Android, :ref:`pyside6-android-deploy` takes care of identifying the necessary permissions needed by
 the application and adding those permissions to the *AndroidManifest.xml* using the
 *<uses-permission>* element.
 
@@ -433,9 +433,7 @@ contain the *usage description* strings for the permissions required. You can ce
 *Info.plist* of the Python framework installation to make the Qt permission API work when running
 a PySide6 application from the terminal. However, this is not recommended. Therefore, the only
 viable solution is to bundle the PySide6 application as a macOS application bundle using
-`pyside6-deploy`_. This macOS application bundle will have its own Info.plist file.
+:ref:`pyside6-deploy`. This macOS application bundle will have its own Info.plist file.
 
 .. _`Blog post`: https://www.qt.io/blog/permission-apis-in-qt-6.5
 .. _`Camera Example`: https://doc.qt.io/qtforpython-6/examples/example_multimedia_camera.html#camera-example
-.. _`pyside6-android-deploy`: https://doc.qt.io/qtforpython-6/gettingstarted/package_details.html#deployment
-.. _`pyside6-deploy`: https://doc.qt.io/qtforpython-6/gettingstarted/package_details.html#deployment
index 4b6c7ffa9f71214b9dcbea0ede4dc1fa5ff7a66e..840f6d8aca24b912cb9a7945aa15ebc9920e7a55 100644 (file)
@@ -3,9 +3,10 @@
 .. toctree::
     :maxdepth: 2
 
-    quickstart.rst
+    gettingstarted.rst
     commercial/index.rst
-    gettingstarted/index.rst
+    building_from_source/index.rst
+    package_details.rst
     api.rst
     tools/index.rst
     tutorials/index.rst
@@ -14,6 +15,7 @@
     deployment/index.rst
     considerations.rst
     developer/index.rst
+    release_notes/index.rst
 
 ..
     Intersphinx references in toctrees is not supported
@@ -25,4 +27,3 @@ Module Index
 ============
 
 * :ref:`modindex`
-
index 46cbb142e863ca1650e81ac0f0bdaba7feb72d21..c02f129e4516468000e14a1e64ab01fd8c02b801 100644 (file)
@@ -118,14 +118,14 @@ Now, build the project using it::
 This step creates a ``build/`` directory with the following structure::
 
     build
-    â””── exe.linux-x86_64-3.7
+    â””── exe.linux-x86_64-3.12
         â””── lib
         â””── main
 
 The first directory inside ``build/`` depends on the platform
-you are using, in this case a ``x86_64`` Linux using Python 3.7.
+you are using, in this case a ``x86_64`` Linux using Python 3.12.
 The structure is the same as previously described, and you can simply
 enter the directory and execute the file::
 
-    cd build/exe.linux-x86_64-3.7
+    cd build/exe.linux-x86_64-3.12
     ./main
index 8b0afa56c4fba6003d60daf7be45217d39af50a6..ceac2065a69b69231b16fd19f26fa48399d116fb 100644 (file)
@@ -117,16 +117,3 @@ for data compression. Then you can run::
 This process takes a bit longer, but in the end you have one executable ``hello.bin``::
 
     ./hello.bin
-
-
-Some Caveats
-============
-
-
-Nuitka issue on macOS
----------------------
-
-Nuitka currently has a problem with the macOS bundle files on current macOS versions.
-That has the effect that ``--standalone`` and ``--onefile`` create a crashing application.
-Older versions which don't have the recent macOS API changes from 2020 will work.
-We are currently trying to fix that problem.
index 53944f6ea6dc76daff84a9a8325482bbbb4bf60c..72e2b9cf447480b58423b649de8feb90ad792843 100644 (file)
@@ -16,24 +16,109 @@ The final output is a `.apk` or a `.aab` file created within the project's sourc
 determines whether a `.apk` or a `.aab` is created.
 
 .. warning:: Currently, users are required to cross-compile Qt for Python to generate the wheels
-    required for a specific Android target architecture. This requirement will disappear when
-    there are official Qt for Python Android wheels (*in progress*). Because of this
-    requirement ``pyside6-android-deploy`` will be considered in **Technical Preview**.
-    Instructions on cross-compiling Qt for Python for Android can be found
-    :ref:`here <cross_compile_android>`.
+    required for `armeabi-v7a` and `x86` Andorid platforms. Instructions on cross-compiling
+    Qt for Python for Android can be found :ref:`here <cross_compile_android>`.
 
-.. note:: ``pyside6-android-deploy`` only works on a Linux host at the moment. This constraint
-    is also because Qt for Python cross-compilation for Android currently only works on Linux
-    systems.
+.. note:: ``pyside6-android-deploy`` only works with a Unix (Linux or macOS) host at the moment.
+
+Prerequisites
+=============
+
+Before using ``pyside6-android-deploy``, ensure that the following prerequisites are met:
+
+.. _android_prerequisites:
+
+Download Android NDK and SDK
+----------------------------
+
+The NDK required corresponds to the NDK version required by the Qt version you are using. See
+`Qt for Android <https://doc.qt.io/qt-6/android.html>`_ for more information.
+
+The easiest way to download the Android NDK is through a script located in the Qt for Python
+repository. To run the script, follow these steps:
+
+#. Clone the Qt for Python repository:
+
+.. code-block:: bash
+
+    git clone https://code.qt.io/pyside/pyside-setup
+
+#. Run the script:
+
+.. code-block:: bash
+
+    cd pyside-setup
+    python tools/cross_compile_android/main.py --download-only --skip-update --auto-accept-license
+
+The script will download the Android NDK and SDK packages required into your home
+directory as a directory called ``.pyside6-android-deploy``. ``pyside6-android-deploy`` will
+automatically detect the NDK and SDK from this cache directory.
+
+If you want to try to download the NDK and SDK manually, you can do so from the following steps
+(for Qt 6.8):
+
+1. Download the sdkmanager using the instructions provided in the `Android Studio
+   documentation <https://developer.android.com/studio/command-line/sdkmanager>`_.
+
+2. Using the sdkmanager download the following packages (for Qt 6.8)::
+
+   "platform-tools", "platforms;android-34", "build-tools;35.0.0"
+
+  and install the NDK using the following command (for Qt 6.8)::
+
+   "ndk;26.1.10909125"
+
+.. note:: The NDK version and the SDK packages required corresponds to the requirements from the
+      Qt version you are using. See `Qt for Android <https://doc.qt.io/qt-6/android.html>`_ for more
+      information.
+
+Download the Qt for Python Android wheels
+-----------------------------------------
+
+There are two ways to download the Qt for Python Android wheels:
+
+1. Download the wheels from the `Qt for Python downloads page`_.
+
+2. Use :ref:`qtpip` download the wheels with the following command:
+
+.. code-block:: bash
+
+    qtpip download PySide6 --android --arch aarch64
+
+for the `aarch64` architecture. The available architectures are `aarch64` and `x86_64`.
 
 How to use it?
 ==============
 
-Like ``pyside6-deploy``, there are :ref:`two different ways <how_pysidedeploy>` with which
+Like :ref:`pyside6-deploy`, there are :ref:`two different ways <how_pysidedeploy>` with which
 you can deploy your PySide6 application using ``pyside6-android-deploy``. The only difference is
 that for ``pyside6-android-deploy`` to work, the main Python entry point file should be named
 ``main.py``.
 
+To deploy the application, run the following command:
+
+.. code-block:: bash
+
+    pyside6-android-deploy --name "MyApp"
+        --wheel-pyside=path_to_downloaded_PySide_wheel
+        --wheel-shiboken=path_to_downloaded_Shiboken_wheel
+        --ndk-path=path_to_ndk
+        --sdk-path=path_to_sdk
+
+The ``--ndk-path`` and ``--sdk-path`` options are optional if you used the script provided in the
+:ref:`android_prerequisites` section to download the NDK and SDK.
+
+For any subsequent deployments, you can use the ``pysidedeploy.spec`` file to control the various
+parameters of the deployment process. The command to deploy the application using the
+``pysidedeploy.spec`` file is:
+
+.. code-block:: bash
+
+    pyside6-android-deploy --config-file path_to_pysidedeploy.spec
+
+The `config-file` option is optional if you are running the command from the project directory
+where ``pysidedeploy.spec`` is located.
+
 .. _pysideandroiddeploy:
 
 pysidedeploy.spec
@@ -176,7 +261,7 @@ done only once per Qt version, irrespective of the number of applications you ar
 Currently, cross-compiling Qt for Python wheels only works with a Linux host. Follow these steps
 to cross-compile Qt for Python Android wheels.
 
-#. `Download <qt_download>`_ and install Qt version for which you would like to create Qt for Python
+#. `Download <qt_download_>`_ and install Qt version for which you would like to create Qt for Python
    wheels.
 
 #. Cloning the Qt for Python repository::
@@ -209,3 +294,4 @@ to cross-compile Qt for Python Android wheels.
 .. _`python-for-android`: https://python-for-android.readthedocs.io/en/latest/
 .. _`qt_download`: https://www.qt.io/download
 .. _`cpython`: https://pypi.org/project/Cython/
+.. _`Qt for Python downloads page`: https://download.qt.io/official_releases/QtForPython/pyside6/
index 6a218fb3a4dc8b0d1197377d6bd820d32b938c8a..534d07072c377bfefff0400844a0225792cc8d53 100644 (file)
@@ -10,11 +10,7 @@ compiles your Python code to C code, and links with libpython to produce the fin
 The final executable produced has a ``.exe`` suffix on Windows, ``.bin`` on Linux and ``.app`` on
 macOS.
 
-.. note:: Although using a virtual environment for Python is recommended for ``pyside6-deploy``, do
-    not add the virtual environment to the application directory you are trying to deploy.
-    ``pyside6-deploy`` will try to package this venv folder and will eventually fail.
-
-.. note:: The default version of Nuitka used with the tool is version ``2.3.2``. This can be
+.. note:: The default version of Nuitka used with the tool is version ``2.5.1``. This can be
     updated to a newer version by updating your ``pysidedeploy.spec`` file.
 
 .. _how_pysidedeploy:
@@ -66,7 +62,7 @@ directory), to take the parameters from that file. This can be done with the fol
 pysidedeploy.spec
 =================
 
-As mentioned in the `Approach 2 <approach_two>`_ above, you can use this file to control the various
+As mentioned in the :ref:`Approach 2 <approach_two>` above, you can use this file to control the various
 parameters of the deployment process. The file has multiple sections, with each section containing
 multiple keys (parameters being controlled) assigned to a value. The advantages of such a file are
 two folds:
@@ -147,6 +143,12 @@ The relevant parameters for ``pyside6-deploy`` are:
 
       NSCameraUsageDescription:CameraAccess
 
+  * ``mode``: Accepts one of the options: ``onefile`` or ``standalone``. The default is ``onefile``.
+    This option corresponds to the mode in which Nuitka is run. The onefile mode creates a single
+    executable file, while the standalone mode creates a directory with the executable and all the
+    necessary files. The standalone mode is useful when you want to distribute the application as a
+    directory with dependencies and other files required by the app.
+
   * ``extra_args``: Any extra Nuitka arguments specified. It is specified as space-separated
     command line arguments i.e. just like how you would specify it when you use Nuitka through
     the command line. By default, it contains the following arguments::
@@ -218,3 +220,29 @@ are required to be installed on the system:
    * - macOS
      - dyld_info
      - Available by default from macOS 12 and upwards
+
+Creating a bug report
+=====================
+
+If you are unsure if the bug is from ``pyside6-deploy`` or ``Nuitka``:
+
+#. Create a bug report in Qt for Python. See instructions
+   `here <https://wiki.qt.io/Qt_for_Python/Reporting_Bugs>`_.
+
+#. Run ``pyside6-deploy`` command with the ``--verbose`` option and replace ``--quiet`` with
+   ``--verbose`` in the ``extra_args`` parameter in the ``pysidedeploy.spec`` file. Attach the
+   output from stdout to the bug report.
+
+#. Attach a minimal example that reproduces the bug with the bug report.
+
+If you think the bug originates from ``Nuitka``:
+
+#. Try using a newer version of ``Nuitka``. You can change this from the ``packages`` parameter in
+   your generated ``pysidedeploy.spec`` file.
+
+#. If the bug persists, create a bug report on the
+   `Nuitka GitHub page <https://github.com/Nuitka/Nuitka/issues>`_.
+
+   * Run ``pyside6-deploy`` with the ``--dry-run`` option to see the actual ``Nuitka`` command
+     generated. Attach the ``Nuitka`` command ran to the bug report.
+   * Follow the Nuitka bug report template to create a bug report.
index 2eb962207e09251a29466c20cf1097f0a5f2f0f6..2ae8702ac5e8ac6e08c53e473382353c0eb24d97 100644 (file)
@@ -58,4 +58,10 @@ Add documentation
 
 - Add entry to ``sources/pyside6/doc/modules.rst``.
 - Add a .qdocconf.in file in ``sources/pyside6/doc/qtmodules``.
-- Add module description ``.rst`` file in ``sources/pyside6/doc/extras``.
+- Add module description ``.rst`` file in ``sources/pyside6/doc/extras``:
+   * Run a full documentation build
+   * Copy the auto-generated file ``<build>/pyside6/doc/base/<Module>/index.rst``
+     to ``sources/pyside6/doc/extras/<Module>/<Module>.rst``
+   * Remove TOC, qmake/CMake instructions and class lists (cf existing files)
+   * Check the references, preferably link to converted documents
+     in PySide or add documents there (see :ref:`maintaining-additionaldocs`).
index b99641f452f3ed54d926370cd4f3c8b64c708f80..18f826bc3334c09d4e1d15c67d5d82b14cb800fd 100644 (file)
@@ -48,6 +48,10 @@ Add a new example
   in the ``doc`` directory containing some description explaining them
   (see the `samplebinding` example).
 
+.. note:: The ``example_gallery`` tools automatically prepends an
+          auto-generated anchor to the example documentation, please use those for
+          linking to the documentation.
+
 Port a Qt example
 -----------------
 
index 732e6b915682e9ad1f8229c793c9ab7001434db1..14199dc1f778d281cf9d4a12a53029464c934e02 100644 (file)
@@ -32,7 +32,7 @@ Tools available to end users
 - Add the tool in ``sources/pyside-tools/pyside_tool.py``.
 - Add the tool in ``build_scripts/__init__.py`` to create the setuptools entry points
   i.e. this enable using the tool from the console as "pyside6-<tool_name>"
-- Add an entry to ``sources/pyside6/doc/gettingstarted/package_details.rst``.
+- Add an entry to ``sources/pyside6/doc/package_details.rst``.
 - Include the necessary Qt binaries explicitly on ``build_scripts/wheel_files.py``
 - Build with ``--standalone``, verify it is working.
 
index 517bd46f188e730760acdbdb533f82c257950def..53e912918130594456af3c4cb5a411462ee9bef3 100644 (file)
@@ -32,12 +32,17 @@ specific information like build instructions should be removed.
 The descriptions may link to tutorials which can be added to additionaldocs.lst
 for webxml extraction.
 
+.. _maintaining-additionaldocs:
+
 Maintaining additionaldocs.lst
 ------------------------------
 
 The file is a list of additional documentation files. These are basically Qt
-tutorials referenced by the documentation. They will receive some Python
-adaption by shiboken/sphinx.
+tutorials referenced by the documentation, for example "Additional Frameworks"
+sections of module files. They will receive some Python adaption by
+shiboken/sphinx.
+
+A line in brackets denotes the output directory.
 
 The list can be created by the below script and some hand-editing. It will find
 almost all documents. Quite a number of them might be unreferenced, but there
@@ -48,16 +53,23 @@ Pages of examples that exist in Python should be removed.
 
     for F in *.webxml
     do
-        echo "$F" | egrep '(-index)|(-module)|(-qmlmodule)\.webxml$' > /dev/null
+        echo "$F" | egrep '(-index|example|cmake|private-module|-changes-qt6|-module.web|-qmlmodule.web)' > /dev/null
         if [ $? -ne 0 ]
         then
             if fgrep '<para>' "$F" > /dev/null # Exclude reference only
             then
-                egrep "(<class )|(<namespace )" $F > /dev/null || echo $F
+                egrep "(<namespace|<class|<struct|<group|<union)" $F > /dev/null || echo $F
             fi
         fi
     done
 
+The overviews go into a directory named ``overviews``. There are also special
+pages containing lists of classes with brief, grouped by function. They mostly
+have a ``<group name="...">`` WebXML element, but may also be hand-written.
+They should be referenced from the module page in a section named `List of
+Classes by Function`. For clarity, the files with a ``<group>`` element are
+placed into a separate directory ``groups``.
+
 Inheritance graphs
 ------------------
 
@@ -67,7 +79,7 @@ The others have main-test drivers for checking.
 
 There are 2 scripts used for determining the inheritance:
 * ``json_inheritance.py`` (env var ``INHERITANCE_FILE``) reads a
-  inheritance.json file containing the class hierarchy generated by
-  shiboken's doc generator.
+inheritance.json file containing the class hierarchy generated by
+shiboken's doc generator.
 
 * ``import_inheritance.py`` actually tries to import the class (legacy)
index 92c84259d93535de92f85546a723338229d6336b..bddd39d91df411b097c6389519803711aa90ce0f 100644 (file)
@@ -19,6 +19,8 @@ Development Topics
    documentation.rst
    adapt_qt.rst
    extras.rst
+   qtasyncio.rst
+   signalmanager.md
 
 Implementation details
 ----------------------
@@ -32,4 +34,5 @@ many features and implementation details that the project has:
    enumfeatures_doc.rst
    limited_api.rst
    signature_doc.rst
+   mypy-correctness.rst
    feature-motivation.rst
diff --git a/sources/pyside6/doc/developer/mypy-correctness.rst b/sources/pyside6/doc/developer/mypy-correctness.rst
new file mode 100644 (file)
index 0000000..41ade93
--- /dev/null
@@ -0,0 +1,188 @@
+.. mypy-correctness:
+
+Improving the Quality of Signatures with mypy
+=============================================
+
+Preliminary
+-----------
+
+The Python Interface files of PySide are generated by a few scripts.
+When ``.pyi`` files were started in 2017, a minimal syntax check was
+possible because these files could be run in ``Python`` itself.
+
+Some changes to the format of ``.pyi`` files made that impossible, leaving
+``PySide``'s ``.pyi`` files quite unchecked for years. Only correct parsing of
+all functions could be checked by the generator.
+
+The introduction of the ``mypy`` tool as a rigorous error checker for the
+generated files brought many improvements, but also some surprizes.
+
+
+Running the mypy Tests
+----------------------
+
+The ``mypy`` tests are automatically run by the Qt company CI framework (COIN).
+When you have ``mypy`` installed, the tests are run when building with tests.
+In debug mode, this can take more than 30 s, therefore we provide the
+translation option
+
+.. code-block:: shell
+
+    --skip-mypy-test
+
+which can be used when repeatedly translating. But note that ``mypy`` has a
+good cache that suppresses analysis of unchanged ``.pyi`` files.
+
+
+Types of mypy Errors
+--------------------
+
+Duplication Errors
+~~~~~~~~~~~~~~~~~~
+
+Many functions have multiple signatures, which are later translated to multiple
+``typing.overload`` versions in the ``.pyi`` file.
+Due to the mapping of ``C++`` functions to ``Python`` it sometimes happens
+that similar ``C++`` functions become ``Python`` duplicates. This was simple
+to filter out, but ``mypy`` still finds duplicates which differ only in parameter
+names. This is now handled by the function ``remove_ambiguous_signatures()``
+in module ``layout`` that compares the so-called ``annotations`` which ignore
+parameter names.
+
+
+Shadowing Errors
+~~~~~~~~~~~~~~~~
+
+A quite subtle error type is the shadowing of multiple signatures. This is due
+to the sequential nature of ``.pyi`` files::
+
+ * In ``C++``, the order of functions does not matter at all. The best fit is
+   automatically used.
+
+ * In Python stub files, the alternatives of multiple signatures are sequentially
+   checked in ``@typing.overload`` chains of functions.
+   This can produce shadowing when an annotation contains another.
+
+An Example: :class:`PySide6.QtCore.QCborSimpleType` is shadowed by int
+when int is listed first. That is due to the Method Resolution Order ``mro()``::
+
+ * int.mro()              [<class 'int'>, <class 'object'>]
+
+ * QCborSimpleType.mro()  [<enum 'QCborSimpleType'>, <enum 'IntEnum'>,
+                           <class 'int'>, <enum 'ReprEnum'>,
+                           <enum 'Enum'>, <class 'object'>]
+
+You see that the ``mro()`` has an ordering effect on the multiple signatures.
+The enum inherits from ``int`` and should come before the ``int`` entry.
+The whole task of bringing the multiple signatures into a conflict-free order
+is a sort of ``Topological Sorting``.
+
+We build a sorting key using the length of the ``mro`` of the argument annotations
+and some additional heuristics. They can be inspected in function ``get_ordering_key()``
+that is called by ``sort_by_inheritance()`` in module ``layout``.
+
+
+Unsolvable Errors
+-----------------
+
+Some errors are pointed out by mypy that we cannot solve. The only chance we have is
+to disable these errors partially or even completely. They are marked in the ``.pyi`` files,
+see below.
+
+
+Contradiction to Qt
+~~~~~~~~~~~~~~~~~~~
+
+Errors are found by mypy where Qt has a different opinion. The error types
+"override" and "overload-overlap" needed to be disabled because we cannot
+change what Qt thinks is right.
+
+Examples:
+
+::
+
+    Error code "override" cannot be fixed because the problem
+    is situated in Qt itself:
+
+        Signature of "open" incompatible with supertype "QFile"
+
+    Error code "overload-overlap" also cannot be fixed because
+    we have no chance to modify return-types:
+
+        Overloaded function signatures 1 and 6 overlap with
+        incompatible return types
+
+They are globally disabled by the comment::
+
+    # mypy: disable-error-code="override, overload-overlap"
+
+Other errors like "misc" are too broad to be prematurely disabled.
+See below how we handle them.
+
+
+Disagreement with __add__ and __iadd__
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are internal rules for ``Python`` which can only be recognized when
+``mypy`` points them out as "misc". There are functions which come in pairs:
+
+.. code-block:: python
+
+    __add__, __iadd__, __sub__, __isub__, __mul__, __imul__, ...
+
+and more. There is this rule::
+
+    if __add__ and __iadd__ exist in a type, the signatures must be the same.
+
+In 95 % this rule is fulfilled, but in a few cases it is not. There we have
+to compute these cases, and if they disagree we generate a disabling ``mypy``
+inline comment "# type: ignore[misc]". You can see this functionality in
+``ExactEnumerator.klass`` of module ``enum_sig``.
+
+
+Disagreement with inconsistent overloads
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If there is a mixed overloading of methods and static or class methods, mypy
+believes this is an error. In a first version, we fixed this rare situation
+by suppressing this "misc" error. But when moving to correct positional-only
+parameters (PEP 570) this suppression created unsolvable follow-up errors.
+The cleaner solution was to remove the static methods and prefer the normal methods.
+
+See function ``is_inconsistent_overload()`` of module
+``layout`` which checks if "self" is always or never an argument.
+
+
+Conclusion and Future
+---------------------
+
+This effort has brought the reported ``mypy`` errors from 601 down to zero, which
+is really an improvement. But there can be done more. Although we now know that we
+are generating syntactically and semantically quite correct files, we still do not know
+whether the real types really fulfil the requirements of ``mypy``.
+
+There is a ``stubtest`` module in ``mypy`` which we might perhaps use to do even
+more tests. These would check if the implementation and stub files agree.
+
+
+Literature
+----------
+
+* `mypy error codes <https://mypy.readthedocs.io/en/stable/error_code_list.html>`__
+    We use these by default enabled codes.
+
+* `typing â€” Support for type hints <https://docs.python.org/3/library/typing.html>`__
+    The Python documentation of the typing module
+
+* `Typing cheat sheet <https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html>`__
+    A quick overview of type hints (hosted at the mypy docs)
+
+* "Type System Reference" section of `the mypy docs <https://mypy.readthedocs.io/en/stable/index.html>`__
+    The Python typing system is standardised via PEPs, so this reference should
+    broadly apply to most Python type checkers. (Some parts may still be specific to mypy.)
+
+* `Static Typing with Python <https://typing.readthedocs.io/en/latest/>`__
+    Type-checker-agnostic documentation written by the community detailing type system features, useful typing related tools and typing best practices.
+
+* `Specification for the Python type system <https://typing.readthedocs.io/en/latest/spec/index.html>`__
+    The complete specification. Quite exhaustive.
diff --git a/sources/pyside6/doc/developer/qtasyncio.rst b/sources/pyside6/doc/developer/qtasyncio.rst
new file mode 100644 (file)
index 0000000..75196d8
--- /dev/null
@@ -0,0 +1,67 @@
+.. _developer-qtasyncio:
+
+QtAsyncio developer notes
+=========================
+
+QtAsyncio is a module that provides integration between Qt and Python's
+`asyncio`_ module. It allows you to run `asyncio` event loops in a Qt
+application, and to use Qt APIs from `asyncio` coroutines.
+
+.. _asyncio: https://docs.python.org/3/library/asyncio.html
+
+As a pure Python module that integrates deeply with another Python
+module, developing for QtAsyncio has some interesting aspects that
+differ from other parts of Qt for Python.
+
+QtAsyncio vs asyncio
+--------------------
+
+QtAsyncio has an interesting property from a developer perspective. As
+one of its purposes is to be a transparent replacement to asyncio's own
+event loop, for any given code that uses asyncio APIs, QtAsyncio will
+(or should!) behave identically to asyncio. (Note that the reverse does
+not apply if we also have Qt code). This is especially handy when
+debugging, as one can run the asyncio code to compare and to check the
+expected behavior. It also helps to debug both QtAsyncio and asyncio
+step by step and compare the program flow to find where QtAsyncio might
+go wrong (as far as their code differences allow). Often, one will also
+be able to implement a unit test by asserting that QtAsyncio's output is
+identical to asyncio's. (Note that a limitation to this is the fact that
+Qt's event loop does not guarantee the order of execution of callbacks,
+while asyncio does.)
+
+The step function
+-----------------
+
+When debugging, a significant amount of time will likely be spent inside
+the ``_step`` method of the ``QAsyncioTask`` class, as it is called
+often and it is where coroutines are executed. You will find that it is
+similar to asyncio's equivalent method, but with some differences. Read
+the in-line comments carefully. Some variables and associated lines of
+code might seem innocuous or even unnecessary, but provide important bug
+fixes.
+
+Keeping QtAsyncio in sync with asyncio
+--------------------------------------
+
+QtAsyncio implements `asyncio's API <https://docs.python.org/3/library/asyncio.html>`_,
+which can change with new releases. As it is part of the standard
+library, this lines up with new Python versions. It is therefore good
+practice to check whether the API has changed for every new Python
+version. This can include new classes or methods, changed signatures for
+existing functions, deprecations or outright removals. For example,
+event loop policies are expected to be deprecated with Python 3.13, with
+subsequent removal in Python 3.15. This removal will in turn require a
+small refactoring to make sure the ``QtAsyncio.run()`` function no
+longer uses the policy mechanism.
+
+asyncio developer guidance
+--------------------------
+
+asyncio's resources go beyond the pure API documentation. E.g., asyncio
+provides its own `developer guidance <https://docs.python.org/3/library/asyncio-dev.html>`_
+that is worth following when developing for QtAsyncio. In addition,
+there is documentation for
+`extending asyncio <https://docs.python.org/3/library/asyncio-extending.html>`_
+that details a few essential points to keep in mind when building your
+own event loop.
diff --git a/sources/pyside6/doc/developer/signalmanager.md b/sources/pyside6/doc/developer/signalmanager.md
new file mode 100644 (file)
index 0000000..1161f6d
--- /dev/null
@@ -0,0 +1,58 @@
+# Signalmanager
+
+# Description
+
+The class `Signalmanager` in *p/sources/pyside6/libpyside* takes care of routing
+Qt signals to the receivers (files *signalmanager.cpp*, *qobjectconnect.cpp*,
+*pysidesignal.cpp*).
+
+There are several kinds of receivers:
+- Slots of C++ classes
+- Slots of `QObject`-derived classes declared in Python using `@Slot`
+- Functions of `QObject`-derived classes declared in Python. They will
+  be turned into proper slots at runtime using the private Qt class
+  `QMetaObjectBuilder`. This causes a warning to be emitted,
+  visible when activating the logging category `qt.pyside.libpyside`.
+- Methods of non-`QObject`-derived classes
+- Other callables (free functions, lambdas, partially bound functions)
+
+Proper Qt connections where `QObject.disconnect()`, `QObject.sender()` and
+`QObject.connectNotify()` work are desirable also for receivers that are not
+`QObject`s. This is achieved by using a `QObject`-derived class
+`GlobalReceiverV2` for a receiver which has a dynamic slot created by
+`QMetaObjectBuilder` to route connected signals to the Python receiver (based
+on internal class `DynamicSlotDataV2`).
+
+The instances of  `GlobalReceiverV2` are stored in a hash on the receiver
+Python objects in `Signalmanager`.
+
+`QMetaObject::connect(QObject*,int,QObject*,int)` is used to make the
+connections based on meta method indexes.
+
+Normally, a reference should be kept on the receiver callable. However, in the
+case of a method of a non-`QObject`-derived class, the connection should be
+automatically severed when the instance is deleted. The callable passed in this
+case (`signal.connect(foo.slot)`) is a partially bound function which has the
+`self` parameter. It is decomposed into the `self` parameter and the method. A
+reference is kept on the method. A weak reference with destruction callback is
+kept for `self`.
+
+## Issues
+
+- [Receiver Leak PYSIDE-1057](https://bugreports.qt.io/browse/PYSIDE-1057)
+- [Partial function receiver Leak PYSIDE-2793](https://bugreports.qt.io/browse/PYSIDE-2793)
+- Various issues related to threading and object deletion, solved by workarounds
+  ([PYSIDE-2646](https://bugreports.qt.io/browse/PYSIDE-2646))
+- Complicated code, hard to maintain
+- Disconnect does not work for `QObject.connect()` with context argument; it also
+  leaks methods
+
+## Plans
+
+Change
+[QObject: Add connect() overload with context arg acab25a3ccb836818e5089b23d40196bc7414b7a](https://codereview.qt-project.org/c/pyside/pyside-setup/+/536298)
+implements a connection based on `QtPrivate::QSlotObjectBase` and
+`QObjectPrivate::connect(QObject*,int,QObject*,QtPrivate::QSlotObjectBase*)`
+(*pysideqslotobject.cpp*). This could in principle enable removing
+`GlobalReceiverV2`, but requires keeping the `QMetaObject::Connection` for
+disconnecting.
index a6c703dabb0f74fe6716b5aefaae701c237c55e5..dceff15fc65b5d5d1f91e693a0b9c40b2c215000 100644 (file)
@@ -3,7 +3,7 @@
 The signature C extension
 =========================
 
-This module is a C extension for CPython 3.5 and up, and CPython 2.7.
+This module is a C extension for CPython 3.X.
 Its purpose is to provide support for the ``__signature__`` attribute
 of builtin PyCFunction objects.
 
@@ -110,26 +110,26 @@ The C++ code involved with the signature module is completely in the file
 shiboken6/libshiboken/signature.cpp . All other functionality is implemented in
 the ``signature`` Python package. It has the following structure::
 
- sources/shiboken6/shibokenmodule/files.dir/shibokensupport
- â”œâ”€â”€ __init__.py
- â”œâ”€â”€ feature.py
- â”œâ”€â”€ fix-complaints.py
- â”œâ”€â”€ shibokensupport.pyproject
- â””── signature
-     â”œâ”€â”€ PSF-3.7.0.txt
-     â”œâ”€â”€ __init__.py
-     â”œâ”€â”€ errorhandler.py
-     â”œâ”€â”€ importhandler.py
-     â”œâ”€â”€ layout.py
-     â”œâ”€â”€ lib
-     â”‚   â”œâ”€â”€ __init__.py
-     â”‚   â”œâ”€â”€ enum_sig.py
-     â”‚   â”œâ”€â”€ pyi_generator.py
-     â”‚   â””── tool.py
-     â”œâ”€â”€ loader.py
-     â”œâ”€â”€ mapping.py
-     â”œâ”€â”€ parser.py
-     â””── qt_attribution.json
+    sources/shiboken6/shibokenmodule/files.dir/shibokensupport
+    â”œâ”€â”€ __init__.py
+    â”œâ”€â”€ feature.py
+    â”œâ”€â”€ fix-complaints.py
+    â”œâ”€â”€ shibokensupport.pyproject
+    â””── signature
+        â”œâ”€â”€ PSF-3.7.0.txt
+        â”œâ”€â”€ __init__.py
+        â”œâ”€â”€ errorhandler.py
+        â”œâ”€â”€ importhandler.py
+        â”œâ”€â”€ layout.py
+        â”œâ”€â”€ lib
+        â”‚   â”œâ”€â”€ __init__.py
+        â”‚   â”œâ”€â”€ enum_sig.py
+        â”‚   â”œâ”€â”€ pyi_generator.py
+        â”‚   â””── tool.py
+        â”œâ”€â”€ loader.py
+        â”œâ”€â”€ mapping.py
+        â”œâ”€â”€ parser.py
+        â””── qt_attribution.json
 
 Really important are the **parser**, **mapping**, **errorhandler**, **enum_sig**,
 **layout** and **loader** modules. The rest is needed to create Python 2 compatibility
@@ -350,6 +350,23 @@ as default content for docstrings.
 This was implemented in ``Qt For Python 5.12.1``.
 
 
+Update and Future of the Signature Module
+-----------------------------------------
+
+.. code-block:: bash
+
+    PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter.
+
+End of 2022, a change to the rlcompleter module made it impossible to further
+support an unofficial ``__signature__`` attribute in PySide. From then on,
+the functionality of signatures was kept by a ``get_signature`` function.
+
+Over the years, the requirements for the correctness of the generated pyi files
+have increased drastically, and a lot of effort went into making the generated
+``.pyi`` files correct for the current ``mypy`` tool. Mode information
+about the kind of errors corrected can be found in the :ref:`mypy-correctnes` section.
+
+
 Literature
 ----------
 
index 8f2d768bf19b4ddbb750b56b10f1b034002c8457..d14c38453e746499090f89c872fe569b73958dfd 100644 (file)
@@ -14,8 +14,6 @@ directive:
 
         from PySide6 import QtCharts
 
-The module also provides `QML types <https://doc.qt.io/qt-6/qtcharts-qmlmodule.html>`_ .
-
 .. note:: An instance of QApplication is required for the QML types as the
    module depends on Qt's \l{Graphics View Framework} for rendering.
    QGuiApplication is not sufficient.
index 75445e1fcfbef8c32ca0d9b04149dc240dbba88f..caf5f5df4cb118f6ead786cb59ad92d17e707edb 100644 (file)
@@ -1,34 +1,36 @@
+PySide6.QtCore.ClassInfo
+========================
+
 .. currentmodule:: PySide6.QtCore
 .. py:decorator:: ClassInfo
 
-This decorator is used to associate extra information to the class, which is available
-using ``QObject.metaObject()``. This information is used by the
-*Qt D-Bus* and *Qt Qml* modules.
-
-The extra information takes the form of a dictionary with key and value in a literal string.
+   This decorator is used to associate extra information to the class, which is available
+   using :meth:`QObject.metaObject`. This information is used by the
+   *Qt D-Bus* and *Qt Qml* modules.
 
-The recommended usage is to provide the key/value using python keyword syntax, where the
-keyword becomes the key, and the provided string becomes the value.
+   The extra information takes the form of a dictionary with key and value in a literal string.
 
-If the key needs to contain special characters (spaces, commas, '::', start with a number, etc),
-it is also possible to pass a python dictionary with arbitrary strings for both the key and
-value and enabling special characters in the key.
+   The recommended usage is to provide the key/value using python keyword syntax, where the
+   keyword becomes the key, and the provided string becomes the value.
 
-.. note:: This decorator is a implementation of the Q_CLASSINFO macro.
+   If the key needs to contain special characters (spaces, commas, '::', start with a number, etc),
+   it is also possible to pass a python dictionary with arbitrary strings for both the key and
+   value and enabling special characters in the key.
 
+   .. note:: This decorator is a implementation of the ``Q_CLASSINFO`` macro.
 
-Example
--------
+   Example
+   -------
 
-.. code-block:: python
+   .. code-block:: python
 
-    # Recommended syntax
-    @ClassInfo(Author='PySide Team', URL='http://www.pyside.org')
-    class MyObject(QObject):
-        ...
+       # Recommended syntax
+       @ClassInfo(Author='PySide Team', URL='http://www.pyside.org')
+       class MyObject(QObject):
+           ...
 
 
-    # Provided to support keys not supported by Python's keyword syntax
-    @ClassInfo({'Some key text $': 'This syntax supports special characters in keys'})
-    class MyObject(QObject):
-        ...
+       # Provided to support keys not supported by Python's keyword syntax
+       @ClassInfo({'Some key text $': 'This syntax supports special characters in keys'})
+       class MyObject(QObject):
+           ...
index 9ed7de4278d904f1e41bbdcb41331d22fd3a2091..da2ccc60a72670059c74e85412766f8f89323f22 100644 (file)
+PySide6.QtCore.Property
+=======================
+
 .. currentmodule:: PySide6.QtCore
 .. py:class:: Property
 
-Detailed Description
---------------------
+    Detailed Description
+    --------------------
+
+    The Property function lets you declare properties that
+    behave both as Qt and Python properties, and have their
+    getters and setters defined as Python functions.
+
+    They are equivalent to the ``Q_PROPERTY`` macro in the `Qt Docs`_.
 
-The Property function lets you declare properties that
-behave both as Qt and Python properties, and have their
-getters and setters defined as Python functions.
+    Here is an example that illustrates how to use this
+    function:
 
-They are equivalent to the ``Q_PROPERTY`` macro in the `Qt Docs`_.
+    .. code-block:: python
+       :linenos:
 
-Here is an example that illustrates how to use this
-function:
+        from PySide6.QtCore import QObject, Property
 
-.. code-block:: python
-   :linenos:
+        class MyObject(QObject):
+            def __init__(self, startval=42):
+                QObject.__init__(self)
+                self.ppval = startval
 
-    from PySide6.QtCore import QObject, Property
+            def readPP(self):
+                return self.ppval
 
-    class MyObject(QObject):
-        def __init__(self, startval=42):
-            QObject.__init__(self)
-            self.ppval = startval
+            def setPP(self, val):
+                self.ppval = val
 
-        def readPP(self):
-            return self.ppval
+            pp = Property(int, readPP, setPP)
 
-        def setPP(self, val):
-            self.ppval = val
+        obj = MyObject()
+        obj.pp = 47
+        print(obj.pp)
 
-        pp = Property(int, readPP, setPP)
+    The full options for ``QtCore.Property`` can be found with ``QtCore.Property.__doc__``:
 
-    obj = MyObject()
-    obj.pp = 47
-    print(obj.pp)
+    .. code-block:: python
 
-The full options for ``QtCore.Property`` can be found with ``QtCore.Property.__doc__``:
+        Property(self, type: type,
+                 fget: Optional[Callable] = None,
+                 fset: Optional[Callable] = None,
+                 freset: Optional[Callable] = None,
+                 fdel: Optional[Callable] = None,
+                 doc: str = '',
+                 notify: Optional[Callable] = None,
+                 designable: bool = True,
+                 scriptable: bool = True,
+                 stored: bool = True, user: bool = False,
+                 constant: bool = False,
+                 final: bool = False) -> PySide6.QtCore.Property
 
-.. code-block:: python
+    Normally, only ``type``, ``fget``and ``fset`` are used.
 
-    Property(self, type: type,
-             fget: Optional[Callable] = None,
-             fset: Optional[Callable] = None,
-             freset: Optional[Callable] = None,
-             fdel: Optional[Callable] = None,
-             doc: str = '',
-             notify: Optional[Callable] = None,
-             designable: bool = True,
-             scriptable: bool = True,
-             stored: bool = True, user: bool = False,
-             constant: bool = False,
-             final: bool = False) -> PySide6.QtCore.Property
 
-Normally, only ``type``, ``fget``and ``fset`` are used.
+    Properties compared with Python properties
+    ------------------------------------------
 
+    ``Python`` has property objects very similar to ``QtCore.Property``.
+    Despite the fact that the latter has an extra ``freset`` function, the usage
+    of properties is almost the same. The main difference is that ``QtCore.Property``
+    requires a ``type`` parameter.
 
-Properties compared with Python properties
-------------------------------------------
 
-``Python`` has property objects very similar to ``QtCore.Property``.
-Despite the fact that the latter has an extra ``freset`` function, the usage
-of properties is almost the same. The main difference is that ``QtCore.Property``
-requires a ``type`` parameter.
+    .. note:: ``Python`` property objects do *not* work in QML; ``QtCore.Property``
+              needs to be used.
 
-In the above example, the following lines would be equivalent properties:
+    In the above example, the following lines would be equivalent properties:
 
-.. code-block:: python
+    .. code-block:: python
 
-    pp = QtCore.Property(int, readPP, setPP)    # PySide version
-    pp = property(readPP, setPP)                # Python version
+        pp = QtCore.Property(int, readPP, setPP)    # PySide version
+        pp = property(readPP, setPP)                # Python version
 
-As you know from the `Python Docs`_, ``Python`` allows to break the property
-creation into multiple steps, using the decorator syntax. We can do this in
-``PySide`` as well:
+    As you know from the `Python Docs`_, ``Python`` allows to break the property
+    creation into multiple steps, using the decorator syntax. We can do this in
+    ``PySide`` as well:
 
-.. code-block:: python
-   :linenos:
+    .. code-block:: python
+       :linenos:
 
-    from PySide6.QtCore import QObject, Property
+        from PySide6.QtCore import QObject, Property
 
-    class MyObject(QObject):
-        def __init__(self, startval=42):
-            QObject.__init__(self)
-            self.ppval = startval
+        class MyObject(QObject):
+            def __init__(self, startval=42):
+                QObject.__init__(self)
+                self.ppval = startval
 
-        @Property(int)
-        def pp(self):
-            return self.ppval
+            @Property(int)
+            def pp(self):
+                return self.ppval
 
-        @pp.setter
-        def pp(self, val):
-            self.ppval = val
+            @pp.setter
+            def pp(self, val):
+                self.ppval = val
 
-    obj = MyObject()
-    obj.pp = 47
-    print(obj.pp)
+        obj = MyObject()
+        obj.pp = 47
+        print(obj.pp)
 
-Please be careful here: The two ``Python`` functions have the same name, intentionally.
-This is needed to let ``Python`` know that these functions belong to the same property.
+    Please be careful here: The two ``Python`` functions have the same name, intentionally.
+    This is needed to let ``Python`` know that these functions belong to the same property.
 
 
-Properties in QML expressions
------------------------------
+    Properties in QML expressions
+    -----------------------------
 
-If you are using properties of your objects in QML expressions,
-QML requires that the property changes are notified. Here is an
-example illustrating how to do this:
+    If you are using properties of your objects in QML expressions,
+    QML requires that the property changes are notified. Here is an
+    example illustrating how to do this:
 
-.. code-block:: python
-   :linenos:
+    .. code-block:: python
+       :linenos:
 
-    from PySide6.QtCore import QObject, Signal, Property
+        from PySide6.QtCore import QObject, Signal, Property
 
-    class Person(QObject):
+        class Person(QObject):
 
-        name_changed = Signal()
+            name_changed = Signal()
 
-        def __init__(self, name):
-            QObject.__init__(self)
-            self._person_name = name
+            def __init__(self, name):
+                QObject.__init__(self)
+                self._person_name = name
 
-        def _name(self):
-            return self._person_name
+            def _name(self):
+                return self._person_name
 
-        name = Property(str, _name, notify=name_changed)
+            name = Property(str, _name, notify=name_changed)
 
 .. _`Python Docs`:  https://docs.python.org/3/library/functions.html?highlight=property#property
 .. _`Qt Docs`:  https://doc.qt.io/qt-6/properties.html
index 7ea35d8af322167ea7bd98081d98c54ba48826e5..cc565e9aaa9b46ac74d75251afb3d0bbf9547b67 100644 (file)
@@ -1,27 +1,30 @@
+PySide6.QtCore.QEnum
+====================
+
 .. currentmodule:: PySide6.QtCore
 .. py:decorator:: QEnum
 
-This class decorator is equivalent to the `Q_ENUM` macro from Qt. The decorator
-is used to register a Python Enum derived class to the meta-object system,
-which is available via `QObject.staticMetaObject`. The enumerator must be in a
-QObject derived class to be registered.
+    This class decorator is equivalent to the `Q_ENUM` macro from Qt. The decorator
+    is used to register a Python Enum derived class to the meta-object system,
+    which is available via `QObject.staticMetaObject`. The enumerator must
+    be in a :class:`QObject` derived class to be registered.
 
-Example
--------
+    Example
+    -------
 
-::
+    ::
 
-    from enum import Enum, auto
+        from enum import Enum, auto
 
-    from PySide6.QtCore import QEnum, QObject
+        from PySide6.QtCore import QEnum, QObject
 
-    class Demo(QObject):
+        class Demo(QObject):
 
-        @QEnum
-        class Orientation(Enum):
-            North, East, South, West = range(4)
+            @QEnum
+            class Orientation(Enum):
+                North, East, South, West = range(4)
 
-See :deco:`QFlag` for registering Python Flag derived classes.
+    See :deco:`QFlag` for registering Python Flag derived classes.
 
-Meanwhile all enums and flags have been converted to Python Enums
-(default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
+    Meanwhile all enums and flags have been converted to Python Enums
+    (default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
index dd4f028006f3c226e5b4fb8069f89eaa2a669658..6b241ffe98f56db8ddab03c433ca15bfb5fcaffa 100644 (file)
@@ -1,74 +1,77 @@
+PySide6.QtCore.QFlag
+====================
+
 .. currentmodule:: PySide6.QtCore
 .. py:decorator:: QFlag
 
-QFlag handles a variation of the Python Enum, the Flag class.
+    QFlag handles a variation of the Python Enum, the Flag class.
 
-Please do not confuse that with the Qt QFlags concept. Python does
-not use that concept, it has its own class hierarchy, instead.
-For more details, see the `Python enum documentation <https://docs.python.org/3/library/enum.html>`_.
+    Please do not confuse that with the Qt QFlags concept. Python does
+    not use that concept, it has its own class hierarchy, instead.
+    For more details, see the `Python enum documentation <https://docs.python.org/3/library/enum.html>`_.
 
-Example
--------
+    Example
+    -------
 
-::
+    ::
 
-    from enum import Flag, auto
+        from enum import Flag, auto
 
-    from PySide6.QtCore import QFlag, QObject
+        from PySide6.QtCore import QFlag, QObject
 
-    class Demo(QObject):
+        class Demo(QObject):
 
-        @QFlag
-        class Color(Flag):
-            RED = auto()
-            BLUE = auto()
-            GREEN = auto()
-            WHITE = RED | BLUE | GREEN
+            @QFlag
+            class Color(Flag):
+                RED = auto()
+                BLUE = auto()
+                GREEN = auto()
+                WHITE = RED | BLUE | GREEN
 
 
-Details about Qt Flags:
------------------------
+    Details about Qt Flags:
+    -----------------------
 
-There are some small differences between Qt flags and Python flags.
-In Qt, we have for instance these declarations:
+    There are some small differences between Qt flags and Python flags.
+    In Qt, we have for instance these declarations:
 
-::
+    ::
 
-    enum    QtGui::RenderHint { Antialiasing, TextAntialiasing, SmoothPixmapTransform,
-                                HighQualityAntialiasing, NonCosmeticDefaultPen }
-    flags   QtGui::RenderHints
+        enum    QtGui::RenderHint { Antialiasing, TextAntialiasing, SmoothPixmapTransform,
+                                    HighQualityAntialiasing, NonCosmeticDefaultPen }
+        flags   QtGui::RenderHints
 
-The equivalent Python notation would look like this:
+    The equivalent Python notation would look like this:
 
-::
+    ::
 
-    @QFlag
-    class RenderHints(enum.Flag)
-        Antialiasing = auto()
-        TextAntialiasing = auto()
-        SmoothPixmapTransform = auto()
-        HighQualityAntialiasing = auto()
-        NonCosmeticDefaultPen = auto()
+        @QFlag
+        class RenderHints(enum.Flag)
+            Antialiasing = auto()
+            TextAntialiasing = auto()
+            SmoothPixmapTransform = auto()
+            HighQualityAntialiasing = auto()
+            NonCosmeticDefaultPen = auto()
 
 
-As another example, the Qt::AlignmentFlag flag has 'AlignmentFlag' as the enum
-name, but 'Alignment' as the type name. Non flag enums have the same type and
-enum names.
+    As another example, the Qt::AlignmentFlag flag has 'AlignmentFlag' as the enum
+    name, but 'Alignment' as the type name. Non flag enums have the same type and
+    enum names.
 
-::
+    ::
 
-    enum Qt::AlignmentFlag
-    flags Qt::Alignment
+        enum Qt::AlignmentFlag
+        flags Qt::Alignment
 
-The Python way to specify this would be
+    The Python way to specify this would be
 
-::
+    ::
 
-    @QFlag
-    class Alignment(enum.Flag):
-        ...
+        @QFlag
+        class Alignment(enum.Flag):
+            ...
 
-See :deco:`QEnum` for registering Python Enum derived classes.
+    See :deco:`QEnum` for registering Python Enum derived classes.
 
-Meanwhile all enums and flags have been converted to Python Enums
-(default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
+    Meanwhile all enums and flags have been converted to Python Enums
+    (default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
index c83a76a8396bc597a9238ecc79801a8852c1a085..bc276ddff53719758e91d4aa92b9c385889e7bb1 100644 (file)
@@ -1,58 +1,64 @@
+PySide6.QtCore.Signal
+=====================
+
 .. currentmodule:: PySide6.QtCore
+
 .. py:class:: Signal
 
-Synopsis
---------
+    Synopsis
+    --------
+
+    Methods
+    ^^^^^^^
 
-Functions
-^^^^^^^^^
+    .. container:: function_list
 
-+-----------------------------------------------------+
-|def :meth:`connect<Signal.connect>` (receiver)       |
-+-----------------------------------------------------+
-|def :meth:`disconnect<Signal.disconnect>` (receiver) |
-+-----------------------------------------------------+
-|def :meth:`emit<Signal.disconnect>` (\*args)         |
-+-----------------------------------------------------+
+        * def :meth:`connect`
+        * def :meth:`disconnect`
+        * def :meth:`emit`
 
-Detailed Description
---------------------
+    Detailed Description
+    --------------------
 
-The :class:`~.Signal` class provides a way to declare and connect Qt
-signals in a pythonic way.
+    The :class:`~.Signal` class provides a way to declare and connect Qt
+    signals in a pythonic way.
 
-.. class:: PySide6.QtCore.Signal([type1 [, type2...]] [, name="" [, arguments=[]]])
+    .. seealso:: :ref:`tutorial_signals_and_slots`
+
+    .. py:method:: __init__([type1 [, type2...]] [, name="" [, arguments=[]]])
 
         :param name: str
         :param arguments: list
 
-``Signal`` takes a list of Python types of the arguments.
+    ``Signal`` takes a list of Python types of the arguments.
+
+    It is possible to use the same signal name with different types by
+    passing a list of tuples representing the signatures, but this is a legacy
+    technique recommended against in new code (see
+    :ref:`overloading-signals-and-slots`).
 
- It is possible to use the same signal name with different types by
- passing a list of tuples representing the signatures, but this is a legacy
- technique recommended against in new code (see
- :ref:`overloading-signals-and-slots`).
+    The optional named argument ``name`` defines the signal name. If nothing is
+    passed, the new signal will have the same name as the variable that it is
+    being assigned to.
 
-The optional named argument ``name`` defines the signal name. If nothing is
-passed, the new signal will have the same name as the variable that it is
-being assigned to.
+    The optional named argument ``arguments`` receives a list of strings
+    denoting the argument names. This is useful for QML applications which
+    may refer to the emitted values by name.
 
-The optional named argument ``arguments`` receives a list of strings
-denoting the argument names. This is useful for QML applications which
-may refer to the emitted values by name.
+    .. py:method:: connect(receiver[, type=Qt.AutoConnection])
 
-.. method:: Signal.connect(receiver[, type=Qt.AutoConnection])
+        :param receiver: Python callable, :deco:`Slot` or :class:`Signal`
+        :param type: :class:`~PySide6.QtCore.Qt.ConnectionType`
 
-    Create a connection between this signal and a `receiver`, the `receiver`
-    can be a Python callable, a :class:`Slot` or a :class:`Signal`.
+    Create a connection between this signal and a `receiver`.
 
-.. method:: Signal.disconnect(receiver)
+    ..  py:method:: disconnect(receiver)
 
-    Disconnect this signal from a `receiver`, the `receiver` can be a Python
-    callable, a :class:`Slot` or a :class:`Signal`.
+        :param receiver: Python callable, :deco:`Slot` or :class:`Signal`
 
-.. method:: Signal.emit(*args)
+    Disconnect this signal from the `receiver`.
 
-    `args` is the arguments to pass to any connected slots, if any.
+    .. py:method:: emit(*args)
 
-.. seealso:: :ref:`signals-and-slots`
+    Emits the signal. `args` is the arguments to pass to any connected slots,
+    if any.
index 98a1d465b546806625d825d9ce4c516c9ba2aaa7..3ed2a245c1b3d20c6adef29f9e4eaefb5f0f4e9c 100644 (file)
@@ -1,42 +1,45 @@
+PySide6.QtCore.Slot
+===================
+
 .. currentmodule:: PySide6.QtCore
 .. py:decorator:: Slot([type1 [, type2...]] [, name="" [, result=None, [tag=""]]])
 
-   :param name: str
-   :param result: type
-   :param tag: str
+       :param name: str
+       :param result: type
+       :param tag: str
 
-``Slot`` takes a list of Python types of the arguments.
+    ``Slot`` takes a list of Python types of the arguments.
 
-The optional named argument ``name`` defines the slot name. If nothing is
-passed, the slot name will be the decorated function name.
+    The optional named argument ``name`` defines the slot name. If nothing is
+    passed, the slot name will be the decorated function name.
 
-The optional named argument ``result`` specifies the return type.
+    The optional named argument ``result`` specifies the return type.
 
-The optional named argument ``tag`` specifies a value to be returned
-by ``QMetaMethod.tag()``.
+    The optional named argument ``tag`` specifies a value to be returned
+    by :meth:`QMetaMethod.tag`.
 
-This implementation is functionally compatible with the PyQt one.
+    This implementation is functionally compatible with the PyQt one.
 
-=======  ===========  ======
-Module   PyQt         PySide
-=======  ===========  ======
-QtCore   pyqtSignal   Signal
-QtCore   pyqtSlot     Slot
-=======  ===========  ======
+    =======  ===========  ======
+    Module   PyQt         PySide
+    =======  ===========  ======
+    QtCore   pyqtSignal   Signal
+    QtCore   pyqtSlot     Slot
+    =======  ===========  ======
 
-.. seealso:: :ref:`signals-and-slots`
+    .. seealso:: :ref:`tutorial_signals_and_slots`
 
-Q_INVOKABLE
------------
+    Q_INVOKABLE
+    -----------
 
-There is no equivalent of the Q_INVOKABLE macro of Qt
-since PySide6 slots can actually have return values.
-If you need to create a invokable method that returns some value,
-declare it as a slot, e.g.:
+    There is no equivalent of the Q_INVOKABLE macro of Qt
+    since PySide6 slots can actually have return values.
+    If you need to create a invokable method that returns some value,
+    declare it as a slot, e.g.:
 
-::
+    ::
 
-    class Foo(QObject):
-        @Slot(float, result=int)
-        def getFloatReturnInt(self, f):
-            return int(f)
+        class Foo(QObject):
+            @Slot(float, result=int)
+            def getFloatReturnInt(self, f):
+                return int(f)
index ac72bc48786277f0c6e0029ad2d22882b4924f09..4f86e5c35141154e3ab5ca6af45989973c0a6f9a 100644 (file)
@@ -49,7 +49,7 @@ printing output.
 In addition, Qt Core provides a platform-independent mechanism for
 storing binary files in the application's executable.
 
-    * :ref:`The Qt Resource System<using_qrc_files>`
+    * :ref:`The Qt Resource System<tutorial_qrcfiles>`
 
 Additional Frameworks
 ^^^^^^^^^^^^^^^^^^^^^
@@ -57,5 +57,15 @@ Additional Frameworks
 Qt Core also provides some of Qt's key frameworks.
 
     * :ref:`The Animation Framework<The-Animation-Framework>`
-    * `JSON Support in Qt <https://doc.qt.io/qt-6/json.html>`_
+    * :ref:`CBOR-Support-in-Qt`
+    * :ref:`JSON-Support-in-Qt`
+    * :ref:`Inter-Process-Communication`
     * :ref:`The Event System<The-Event-System>`
+    * :ref:`Application-Permissions`
+
+List of Classes by Function
+---------------------------
+
+    * :ref:`Animation-Framework`
+    * :ref:`Event-Classes`
+    * :ref:`Input-Output-and-Networking`
index 66e2d95235c82ddfa9626af353a8af45acc62314..a90b6a6e8986128554e5df9346773992499172f6 100644 (file)
@@ -19,8 +19,6 @@ directive:
 
     import PySide6.QtDataVisualization
 
-The module also provides `QML types <https://doc.qt.io/qt-6/qtdatavisualization-qmlmodule.html>`_ .
-
 Articles and Guides
 ^^^^^^^^^^^^^^^^^^^
 
index a3e5ef5115c4c25e2daad1fdac6a069c06417a00..c8c6735d956cf3fff88699f8f055daa6d7ff4e98 100644 (file)
@@ -1,9 +1,12 @@
+PySide6.QtDesigner.QPyDesignerContainerExtension
+================================================
+
 .. currentmodule:: PySide6.QtDesigner
 .. py:class:: QPyDesignerContainerExtension
 
-QPyDesignerContainerExtension is the base class for implementing
-`QDesignerContainerExtension class`_
-for a *Qt Widgets Designer* custom widget plugin in Python.
-It provides the required inheritance from **QObject**.
+    QPyDesignerContainerExtension is the base class for implementing
+    `QDesignerContainerExtension class`_
+    for a *Qt Widgets Designer* custom widget plugin in Python.
+    It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
 
 .. _QDesignerContainerExtension class: https://doc.qt.io/qt-6/qdesignercontainerextension.html
index 615ca4eecc68ecfb477ec705c56a981ce4947653..45eec5bd58e8ad5e66ff5dccadb6956eb0bd7432 100644 (file)
@@ -1,52 +1,53 @@
+PySide6.QtDesigner.QPyDesignerCustomWidgetCollection
+====================================================
+
 .. currentmodule:: PySide6.QtDesigner
 .. py:class:: QPyDesignerCustomWidgetCollection
 
-Synopsis
---------
+    Synopsis
+    --------
+
+    Methods
+    ^^^^^^^
 
-Functions
-^^^^^^^^^
+    .. container:: function_list
 
-+------------------------------------------------------------------------------------------------+
-|def :meth:`registerCustomWidget<QPyDesignerCustomWidgetCollection.registerCustomWidget>` (type) |
-+------------------------------------------------------------------------------------------------+
-|def :meth:`addCustomWidget<QPyDesignerCustomWidgetCollection.addCustomWidget>` (custom_widget)  |
-+------------------------------------------------------------------------------------------------+
+        def :meth:`registerCustomWidget`
+        def :meth:`addCustomWidget`
 
-Detailed Description
---------------------
+    Detailed Description
+    --------------------
 
-The :class:`~.QPyDesignerCustomWidgetCollection` implements
-`QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
-and provides static helper functions for registering custom widgets by
-type or by implementing
-`QDesignerCustomWidgetInterface`_ .
+    The :class:`~.QPyDesignerCustomWidgetCollection` implements
+    `QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
+    and provides static helper functions for registering custom widgets by
+    type or by implementing `QDesignerCustomWidgetInterface`_ .
 
-The usage is explained in :ref:`designer_custom_widgets`.
+    The usage is explained in :ref:`designer_custom_widgets`.
 
-.. py:staticmethod:: QPyDesignerCustomWidgetCollection.registerCustomWidget(type[, xml=""[, tool_tip=""[, icon=""[, group=""[container=False]]]]])
+    .. py:staticmethod:: QPyDesignerCustomWidgetCollection.registerCustomWidget(type[, xml=""[, tool_tip=""[, icon=""[, group=""[container=False]]]]])
 
-   Registers an instance of a Python-implemented QWidget by type with
-   *Qt Widgets Designer*.
+        :param str xml: A snippet of XML code in ``.ui`` format that specifies how the widget is created and sets initial property values.
+        :param str tool_tip: Tool tip to be shown in the widget box.
+        :param str icon: Path to an icon file be shown in the widget box.
+        :param str group: Category for grouping widgets in the widget box.
+        :param str module: Module name for generating the import code by `uic <https://doc.qt.io/qt-6/uic.html>`_ .
+        :param bool container: Indicates whether the widget is a container like `QGroupBox`, that is, child widgets can be placed on it.
 
-   The optional keyword arguments correspond to the getters of
-   `QDesignerCustomWidgetInterface`_ :
+        Registers an instance of a Python-implemented QWidget by type with
+        *Qt Widgets Designer*.
 
-   :param str xml: A snippet of XML code in ``.ui`` format that specifies how the widget is created and sets initial property values.
-   :param str tool_tip: Tool tip to be shown in the widget box.
-   :param str icon: Path to an icon file be shown in the widget box.
-   :param str group: Category for grouping widgets in the widget box.
-   :param str module: Module name for generating the import code by `uic <https://doc.qt.io/qt-6/uic.html>`_ .
-   :param bool container: Indicates whether the widget is a container like `QGroupBox`, that is, child widgets can be placed on it.
+        The optional keyword arguments correspond to the getters of
+        `QDesignerCustomWidgetInterface`_ :
 
-   .. seealso::  :meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`
+        .. seealso::  :meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`
 
-.. py:staticmethod:: QPyDesignerCustomWidgetCollection.addCustomWidget(custom_widget)
+    .. py:staticmethod:: QPyDesignerCustomWidgetCollection.addCustomWidget(custom_widget)
 
-    Adds a custom widget (implementation of
-    `QDesignerCustomWidgetInterface`_)
-    with *Qt Widgets Designer*.
+        :param QDesignerCustomWidgetInterface custom_widget: Custom widget instance
 
-   :param QDesignerCustomWidgetInterface custom_widget: Custom widget instance
+        Adds a custom widget (implementation of
+        `QDesignerCustomWidgetInterface`_)
+        with *Qt Widgets Designer*.
 
 .. _QDesignerCustomWidgetInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html
index ef23562219ab0ebf565749fb649108429e921797..711d4969c3255118c7f79e78c1e0fab3e11ff186 100644 (file)
@@ -1,9 +1,12 @@
+PySide6.QtDesigner.QPyDesignerMemberSheetExtension
+==================================================
+
 .. currentmodule:: PySide6.QtDesigner
 .. py:class:: QPyDesignerMemberSheetExtension
 
-QPyDesignerMemberSheetExtension is the base class for implementing
-`QDesignerMemberSheetExtension class`_
-for a *Qt Widgets Designer* custom widget plugin in Python.
-It provides the required inheritance from **QObject**.
+    QPyDesignerMemberSheetExtension is the base class for implementing
+    `QDesignerMemberSheetExtension class`_
+    for a *Qt Widgets Designer* custom widget plugin in Python.
+    It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
 
 .. _QDesignerMemberSheetExtension class: https://doc.qt.io/qt-6/qdesignermembersheetextension.html
index e5e13122d25ecf4197557d13e4dc4b151c8bc6ff..2e11988d78c7a53ddeab06c60cc01253c5608a70 100644 (file)
@@ -1,9 +1,12 @@
+PySide6.QtDesigner.QPyDesignerTaskMenuExtension
+===============================================
+
 .. currentmodule:: PySide6.QtDesigner
 .. py:class:: QPyDesignerTaskMenuExtension
 
-QPyDesignerTaskMenuExtension is the base class for implementing
-`QDesignerTaskMenuExtension class`_
-for a *Qt Widgets Designer* custom widget plugin in Python.
-It provides the required inheritance from **QObject**.
+    QPyDesignerTaskMenuExtension is the base class for implementing
+    `QDesignerTaskMenuExtension class`_
+    for a *Qt Widgets Designer* custom widget plugin in Python.
+    It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
 
 .. _QDesignerTaskMenuExtension class: https://doc.qt.io/qt-6/qdesignertaskmenuextension.html
index 8341ff45319d8adf6c5d612616c87244b3fdefd1..9b1b0d9dd4e5d873698dfde9ca4d2ec10138eda2 100644 (file)
@@ -13,9 +13,10 @@ Application Windows
 ^^^^^^^^^^^^^^^^^^^
 
 The most important classes in the Qt GUI module are
-:class:`QGuiApplication<PySide6.QtGui.QGuiApplication>` and
-:class:`QWindow<PySide6.QtGui.QWindow>` . A Qt application that wants
-to show content on screen will need to make use of these.
+:class:`QGuiApplication<PySide6.QtGui.QGuiApplication>`,
+:class:`~PySide6.QtGui.QWindow` and :class:`~PySide6.QtGui.QScreen`.
+A Qt application that wants to show content on screen will need to make
+use of these.
 :class:`QGuiApplication<PySide6.QtGui.QGuiApplication>` contains the
 main event loop, where all events from the window system and other
 sources are processed and dispatched. It also handles the
@@ -99,32 +100,6 @@ with :class:`QPainter<PySide6.QtGui.QPainter>` and
 have OpenGL hardware-accelerated 2D graphics by sacrificing some of
 the visual quality.
 
-Vulkan Integration
-^^^^^^^^^^^^^^^^^^
-
-Qt GUI has support for the `Vulkan <https://www.khronos.org/vulkan/>`_
-API. Qt applications require the presence of the `LunarG Vulkan SDK
-<https://www.lunarg.com/vulkan-sdk/>`_ .
-
-On Windows, the SDK sets the environment variable ``VULKAN_SDK``\,
-which will be detected by the ``configure`` script.
-
-On Android, Vulkan headers were added in API level 24 of the NDK.
-
-Relevant classes:
-
-* QVulkanDeviceFunctions
-    * :class:`QVulkanExtension<~.QVulkanExtension>`
-    * QVulkanFunctions
-    * :class:`QVulkanInfoVector<~.QVulkanInfoVector>`
-    * :class:`QVulkanInstance<~.QVulkanInstance>`
-    * :class:`QVulkanWindow<~.QVulkanWindow>`
-    * :class:`QVulkanWindowRenderer<~.QVulkanWindowRenderer>`
-
-For more information, see the
-:ref:`Hello Vulkan Widget Example<Hello-Vulkan-Widget-Example>` and the
-:ref:`Hello Vulkan Window Example<Hello-Vulkan-Window-Example>` .
-
 Drag and Drop
 ^^^^^^^^^^^^^
 
@@ -140,3 +115,10 @@ directive:
 ::
 
     import PySide6.QtGui
+
+List of Classes by Function
+---------------------------
+
+    * :ref:`Painting-Classes`
+    * :ref:`Rendering-in-3D`
+    * :ref:`Rich-Text-Processing-APIs`
index 527c2427bec67c103f6305cc4d64b16d66c56bc2..7d7739fce214495d94aea0eeabe3aa35a03b56a8 100644 (file)
@@ -29,8 +29,6 @@ directive:
 
     import PySide6.QtMultimedia
 
-The module also provides QML `types <https://doc.qt.io/qt-6/qtmultimedia-qmlmodule.html>`_ .
-
 Overviews and Important Topics
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index 18d8b3db278921dd26a3c5eee2a2c5fa79e1cf4a..41dcfc4d2d79883f962e4336e906ab139d31a7be 100644 (file)
@@ -28,3 +28,8 @@ These articles contain information about Qt Network setup and about applications
 Also note shipping OpenSSL might cause
 `Import and Export Restrictions <https://doc.qt.io/qt-6/ssl.html#import-and-export-restrictions>`_
 to apply.
+
+List of Classes by Function
+---------------------------
+
+    * :ref:`Network-Programming-API`
index aa699a7d7908d1c6e482e0eb60155ecc514bd3fe..bf4c3784242e75ea72cf2569acc8d9d4eb395cc5 100644 (file)
@@ -23,4 +23,3 @@ the whole Qt API for non-OpenGL-specific GUI functionality.
 The QtOpenGL module is available on Windows, X11 and Mac OS X. Qt for
 Embedded Linux and OpenGL supports OpenGL ES (OpenGL for Embedded
 Systems).
-
index 800e369faf38d006c0826ac73881233955124cb0..ee4d32c49a8dd15ce974c3173bed13dfbf52785b 100644 (file)
@@ -23,8 +23,6 @@ directive:
 
     import PySide6.QtPositioning
 
-The module also provides `QML types <https://doc.qt.io/qt-6/qtpositioning-qmlmodule.html>`_ .
-
 To load the Qt Positioning module, add the following statement to your .qml files
 
     ::
index 146ef6a9f56b5b53d7e787b03179a5d9a1e8decc..9b77199a9f45644e864f07c3f157fe8c17329222 100644 (file)
@@ -13,3 +13,8 @@ directive:
 ::
 
     import PySide6.QtPrintSupport
+
+List of Classes by Function
+---------------------------
+
+    * :ref:`Printer-and-Printing-APIs`
index eaa580c68774dab3d41ec6df63b29d683b129395..8423936e4058159e2f8bc81f41624179eac117e3 100644 (file)
@@ -1,10 +1,13 @@
+PySide6.QtQml.ListProperty
+==========================
+
 .. currentmodule:: PySide6.QtQml
 .. py:class:: ListProperty
 
     The ``ListProperty`` class allows applications to expose list-like properties of
     :class:`~PySide6.QtCore.QObject`-derived classes to QML.
-    The usage is shown in the :ref:`qml-object-and-list-property-types-example`
-    and the :ref:`qml-chapter5-listproperties` example.
+    The usage is shown in the :ref:`example_qml_tutorials_extending-qml-advanced_properties`
+    and the :ref:`example_qml_tutorials_extending-qml_chapter5-listproperties` example.
 
     .. py:method:: __init__(type, append, count=None, at=None, clear=None, removeLast=None, doc="", notify=None, designable=True, scriptable=True, stored=True, user=False, constant=False, final=False)
 
index 10dde6b9ab178e98fbea95413da48f8fedeab883..19773ea55fc3aa7019244382e3d443f863c08aaf 100644 (file)
@@ -1,9 +1,9 @@
 .. currentmodule:: PySide6.QtQml
 .. py:class:: QPyQmlParserStatus
 
-QPyQmlParserStatus is the base class for implementing
-`QQmlParserStatus class`_ .
+    QPyQmlParserStatus is the base class for implementing
+    `QQmlParserStatus class`_ .
 
-It provides the required inheritance from **QObject**.
+    It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
 
 .. _QQmlParserStatus class: https://doc.qt.io/qt-6/qqmlparserstatus.html
index bf7f8e98f67718806a70c09ae8a4004ad924a982..86431a5821d5a2d699fa04dda0eb08f1ecd4ae90 100644 (file)
@@ -1,9 +1,9 @@
 .. currentmodule:: PySide6.QtQml
 .. py:class:: QPyQmlPropertyValueSource
 
-QPyQmlPropertyValueSource is the base class for implementing
-`QQmlPropertyValueSource class`_ .
+    QPyQmlPropertyValueSource is the base class for implementing
+    `QQmlPropertyValueSource class`_ .
 
-It provides the required inheritance from **QObject**.
+    It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
 
 .. _QQmlPropertyValueSource class: https://doc.qt.io/qt-6/qqmlpropertyvaluesource.html
index 44aa0ea21a46c8cf81fe38a706ed476e620f55d1..e2c9be192adccd046a3b46d2fd624581262f5706 100644 (file)
@@ -1,17 +1,20 @@
+PySide6.QtQml.QmlAnonymous
+==========================
+
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlAnonymous
 
-Declares the enclosing type to be available, but anonymous in QML. The type
-cannot be created or used to declare properties in QML, but when passed from
-C++, it is recognized. In QML, you can use properties of this type if they
-are declared in C++.
+    Declares the enclosing type to be available, but anonymous in QML. The type
+    cannot be created or used to declare properties in QML, but when passed from
+    C++, it is recognized. In QML, you can use properties of this type if they
+    are declared in C++.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
-    @QmlAnonymous
-    class ClassForQml(QObject):
-        # ...
+        @QmlAnonymous
+        class ClassForQml(QObject):
+            # ...
index 4331b03f9590401ea93102e209c2852cfe3e9235..60e006acb1c23c8f01152d852c90c9d90bf4a843 100644 (file)
@@ -1,35 +1,38 @@
+PySide6.QtQml.QmlAttached
+=========================
+
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlAttached
 
-This decorator declares that the enclosing type attaches the type passed as
-an attached property to other types. This takes effect if the type is exposed
-to QML using a ``QmlElement()`` or ``@QmlNamedElement()`` decorator.
+    This decorator declares that the enclosing type attaches the type passed as
+    an attached property to other types. This takes effect if the type is exposed
+    to QML using a :deco:`QmlElement` or :deco:`QmlNamedElement` decorator.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
-    @QmlAnonymous
-    class LayoutAttached(QObject):
-        @Property(QMargins)
-        def margins(self):
-           ...
+        @QmlAnonymous
+        class LayoutAttached(QObject):
+            @Property(QMargins)
+            def margins(self):
+               ...
 
-    @QmlElement()
-    @QmlAttached(LayoutAttached)
-    class Layout(QObject):
-          ...
+        @QmlElement()
+        @QmlAttached(LayoutAttached)
+        class Layout(QObject):
+              ...
 
-Afterwards the class may be used in QML:
+    Afterwards the class may be used in QML:
 
-.. code-block:: javascript
+    .. code-block:: javascript
 
-      import com.library.name 1.0
+          import com.library.name 1.0
 
-      Layout {
-         Widget {
-             Layout.margins: [2, 2, 2, 2]
-         }
-      }
+          Layout {
+             Widget {
+                 Layout.margins: [2, 2, 2, 2]
+             }
+          }
index 66397b2d9be20f71de2e177abf9cc0b42ce22a39..ec4c110650d478d162f53cd6ad2ce4d694eb8d38 100644 (file)
@@ -1,25 +1,28 @@
+PySide6.QtQml.QmlElement
+========================
+
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlElement
 
-This decorator registers a class it is attached to for use in QML, using
-global variables to specify the import name and version.
+    This decorator registers a class it is attached to for use in QML, using
+    global variables to specify the import name and version.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
-    @QmlElement
-    class ClassForQml(QObject):
-        # ...
+        @QmlElement
+        class ClassForQml(QObject):
+            # ...
 
-Afterwards the class may be used in QML:
+    Afterwards the class may be used in QML:
 
-.. code-block:: python
+    .. code-block:: python
 
-    import com.library.name 1.0
+        import com.library.name 1.0
 
-    ClassForQml {
-       // ...
-    }
+        ClassForQml {
+           // ...
+        }
index af113a9c80479c0aa4f0f92464a2a38c9657ffb1..eaeb77d1c0e9a6cb313c78fd25890769ebfb8c61 100644 (file)
@@ -1,32 +1,35 @@
+PySide6.QtQml.QmlExtended
+=========================
+
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlExtended
 
-Declares that the enclosing type uses the type passed as an extension to
-provide further properties, methods, and enumerations in QML. This takes effect
-if the type is exposed to QML using a ``QmlElement()`` or ``QmlNamedElement()``
-decorator.
+    Declares that the enclosing type uses the type passed as an extension to
+    provide further properties, methods, and enumerations in QML. This takes effect
+    if the type is exposed to QML using a :deco:`QmlElement` or :deco:`QmlNamedElement`
+    decorator.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
-    class LineEditExtension(QObject):
-        pass
+        class LineEditExtension(QObject):
+            pass
 
-    @QmlNamedElement("QLineEdit")
-    @QmlExtended(LineEditExtension)
-    @QmlForeign(QLineEdit)
-    class LineEditForeign(QObject):
-        ...
+        @QmlNamedElement("QLineEdit")
+        @QmlExtended(LineEditExtension)
+        @QmlForeign(QLineEdit)
+        class LineEditForeign(QObject):
+            ...
 
-Afterwards the class may be used in QML:
+    Afterwards the class may be used in QML:
 
-.. code-block:: javascript
+    .. code-block:: javascript
 
-      import com.library.name 1.0
+          import com.library.name 1.0
 
-      QLineEdit {
-          left_margin: 10
-      }
+          QLineEdit {
+              left_margin: 10
+          }
index 90b821e9b8bf0aa4645b86079729ff74c7d3ef01..343318b9ae3e2371a76a7fc3dc575cd3598f754c 100644 (file)
@@ -1,28 +1,31 @@
+PySide6.QtQml.QmlForeign
+========================
+
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlForeign
 
-This decorator can be used to change the type that is created by QML.
+    This decorator can be used to change the type that is created by QML.
 
-This is useful for registering types that cannot be amended by adding the
-QmlElement decorator, for example because they belong to 3rdparty libraries.
+    This is useful for registering types that cannot be amended by adding the
+    :deco:`QmlElement` decorator, for example because they belong to 3rdparty libraries.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
-    @QmlNamedElement("QWidget")
-    @QmlForeign(QWidget)
-    class ForeignWidgetHelperClass(QObject):
-          ...
+        @QmlNamedElement("QWidget")
+        @QmlForeign(QWidget)
+        class ForeignWidgetHelperClass(QObject):
+              ...
 
-Afterwards the class may be used in QML:
+    Afterwards the class may be used in QML:
 
-.. code-block:: javascript
+    .. code-block:: javascript
 
-    import com.library.name 1.0
+        import com.library.name 1.0
 
-    QWidget {
-        // ...
-    }
+        QWidget {
+            // ...
+        }
index 79eb9d7adfe188a7e0b677495e4a76e4256537ae..1ce8bd796f1ae17313813fefffb4ce8eafd59a13 100644 (file)
@@ -1,26 +1,26 @@
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlNamedElement
 
-This decorator registers a class it is attached to for use in QML under
-a name different from the class name, using global variables to specify
-the import name and version.
+    This decorator registers a class it is attached to for use in QML under
+    a name different from the class name, using global variables to specify
+    the import name and version.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
-    @QmlNamedElement("ClassForQml")
-    class ClassWithSomeName(QObject):
-          ...
+        @QmlNamedElement("ClassForQml")
+        class ClassWithSomeName(QObject):
+              ...
 
-Afterwards the class may be used in QML:
+    Afterwards the class may be used in QML:
 
-.. code-block:: javascript
+    .. code-block:: javascript
 
-    import com.library.name 1.0
+        import com.library.name 1.0
 
-    ClassForQml {
-       // ...
-    }
+        ClassForQml {
+           // ...
+        }
index 402c18d55deaf1ddc47100658aa90b95fafe4ad5..1c517347851079b745ea013a0ca02565d66de5a8 100644 (file)
@@ -1,33 +1,34 @@
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlSingleton
 
-Declares the decorated type to be a singleton in QML. This only takes effect if
-the type is a QObject and is available in QML (by having a QmlElement decorator).
-The QQmlEngine will try to create a singleton instance using the type's default
-constructor.
+    Declares the decorated type to be a singleton in QML. This only takes effect if
+    the type is a :class:`~PySide6.QtCore.QObject` and is available in QML
+    (by having a :deco:`QmlElement` decorator).
+    The :class:`QQmlEngine` will try to create a singleton instance using the type's default
+    constructor.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
-    @QmlElement
-    @QmlSingleton
-    class ClassForQml(QObject):
-        ...
+        @QmlElement
+        @QmlSingleton
+        class ClassForQml(QObject):
+            ...
 
-It is also possible to use a static ``create()`` method which receives
-the engine as a parameter:
+    It is also possible to use a static ``create()`` method which receives
+    the engine as a parameter:
 
-.. code-block:: python
+    .. code-block:: python
 
-    @QmlElement
-    @QmlSingleton
-    class ClassForQml(QObject):
+        @QmlElement
+        @QmlSingleton
+        class ClassForQml(QObject):
 
-        @staticmethod
-        def create(engine):
-            ...
+            @staticmethod
+            def create(engine):
+                ...
 
-.. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``.
+    .. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``.
index b7a28801fbdf02727e7a1864013f1d5032e7480e..0810b123cf956e2c2d4e5f4164694de8b3b49a41 100644 (file)
@@ -1,26 +1,26 @@
 .. currentmodule:: PySide6.QtQml
 .. py:decorator:: QmlUncreatable
 
-Declares that the decorated type shall not be creatable from QML. This takes
-effect if the type is available in QML, by a preceding ``QmlElement``
-decorator. The reason will be emitted as error message if an attempt to create
-the type from QML is detected.
+    Declares that the decorated type shall not be creatable from QML. This takes
+    effect if the type is available in QML, by a preceding :deco:`QmlElement`
+    decorator. The reason will be emitted as error message if an attempt to create
+    the type from QML is detected.
 
-Some QML types are implicitly uncreatable, in particular types exposed with
-``QmlAnonymous``.
+    Some QML types are implicitly uncreatable, in particular types exposed with
+    :deco:`QmlAnonymous`.
 
-Passing None or no argument will cause a standard message to be used instead.
+    Passing None or no argument will cause a standard message to be used instead.
 
-.. code-block:: python
+    .. code-block:: python
 
-    QML_IMPORT_NAME = "com.library.name"
-    QML_IMPORT_MAJOR_VERSION = 1
-    QML_IMPORT_MINOR_VERSION = 0 # Optional
+        QML_IMPORT_NAME = "com.library.name"
+        QML_IMPORT_MAJOR_VERSION = 1
+        QML_IMPORT_MINOR_VERSION = 0 # Optional
 
 
-    @QmlElement
-    @QmlUncreatable("BaseClassForQml is an abstract base class")
-    class BaseClassForQml(QObject):
-        ...
+        @QmlElement
+        @QmlUncreatable("BaseClassForQml is an abstract base class")
+        class BaseClassForQml(QObject):
+            ...
 
-.. note:: The order of the decorators matters; ``QmlUncreatable`` needs to be preceded by ``QmlElement``.
+    .. note:: The order of the decorators matters; ``QmlUncreatable`` needs to be preceded by ``QmlElement``.
index 66f736f02ba85ab211a63f8d0f22bf6e3f9d6d72..9d20ee71aa2588cb892d696060dd33fcd1692d5c 100644 (file)
@@ -1,20 +1,11 @@
-The Qt QML module defines and implements the QML language
+The Qt Qml module implements the QML language and offers APIs to register types
+for it.
 
-The Qt QML module provides a framework for developing applications and
-libraries with the QML language. It defines and implements the language and
-engine infrastructure, and provides an API to enable application developers to
-extend the QML language with custom types and integrate QML code with
-JavaScript and C++. The Qt QML module provides both a `QML API
-<https://doc.qt.io/qt-6/qtqml-qmlmodule.html>`_ and a `C++ API
-<https://doc.qt.io/qt-6/qtqml-module.html>`_ .
-
-Note that while the Qt QML module provides the language and infrastructure for
-QML applications, the :ref:`Qt Quick<Qt-Quick>` module provides many visual
-components, model-view support, an animation framework, and much more for
-building user interfaces.
-
-For those new to QML and Qt Quick, please see QML Applications for an
-introduction to writing QML applications.
+The Qt Qml module provides a framework for developing applications and
+libraries with the :ref:`The-QML-Reference` . It defines and implements the
+language and engine infrastructure, and provides an API to enable application
+developers to register custom QML types and modules and integrate QML code with
+JavaScript and Python. The Qt Qml module provides both a QML API a Python API.
 
 Using the Module
 ^^^^^^^^^^^^^^^^
@@ -26,50 +17,16 @@ directive:
 
     import PySide6.QtQml
 
-The module also provides `QML types <https://doc.qt.io/qt-6/qtqml-qmlmodule.html>`_ .
-
-QML and QML Types
-^^^^^^^^^^^^^^^^^
-
-The Qt QML module contains the QML framework and important QML types used in
-applications. The constructs of QML are described in the
-:ref:`The QML Reference<The-QML-Reference>` .
-
-In addition to the :ref:`QML Basic Types<QML-Basic-Types>` , the module comes
-with the following QML object types:
-
-    * `Component <https://doc.qt.io/qt-6/qml-qtqml-component.html>`_
-    * `QtObject <https://doc.qt.io/qt-6/qml-qtqml-qtobject.html>`_
-    * `Binding <https://doc.qt.io/qt-6/qml-qtqml-binding.html>`_
-    * `Connections <https://doc.qt.io/qt-6/qml-qtqml-connections.html>`_
-    * `Timer <https://doc.qt.io/qt-6/qml-qtqml-timer.html>`_
-
-The `Qt <https://doc.qt.io/qt-6/qml-qtqml-qt.html>`_ global object provides
-useful enums and functions for various QML types.
-
-Lists and Models
-^^^^^^^^^^^^^^^^
-
-New in Qt 5.1, the model types are moved to a submodule, ``QtQml.Models``\. The
-Qt QML Models page has more information.
-
-    * DelegateModel
-    * DelegateModelGroup
-    * ListElement
-    * ListModel
-    * ObjectModel
-
-JavaScript Environment for QML Applications
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Registering QML Types and QML Modules
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-JavaScript expressions allow QML code to contain application logic. Qt QML
-provides the framework for running JavaScript expressions in QML and from C++.
+See :ref:`tutorial_qmlintegration`.
 
-These sections are from :ref:`The QML Reference<The-QML-Reference>` .
+Tweaking the engine
+^^^^^^^^^^^^^^^^^^^
 
-    * `Integrating QML and JavaScript <https://doc.qt.io/qt-6/qtqml-javascript-topic.html>`_
-    * `Using JavaScript Expressions with QML <https://doc.qt.io/qt-6/qtqml-javascript-expressions.html>`_
-    * `Dynamic QML Object Creation from JavaScript <https://doc.qt.io/qt-6/qtqml-javascript-dynamicobjectcreation.html>`_
-    * `Defining JavaScript Resources In QML <https://doc.qt.io/qt-6/qtqml-javascript-resources.html>`_
-    * `Importing JavaScript Resources In QML <https://doc.qt.io/qt-6/qtqml-javascript-imports.html>`_
-    * `JavaScript Host Environment <https://doc.qt.io/qt-6/qtqml-javascript-hostenvironment.html>`_
+There are a number of knobs you can turn to customize the behavior of the QML
+engine. The page on :ref:`Configuring-the-JavaScript-Engine` lists the
+environment variables you may use to this effect. The description of
+:ref:`The-QML-Disk-Cache` describes the options related to how your QML
+components are compiled and loaded.
diff --git a/sources/pyside6/doc/extras/QtQuick.rst b/sources/pyside6/doc/extras/QtQuick.rst
new file mode 100644 (file)
index 0000000..1027c1f
--- /dev/null
@@ -0,0 +1,82 @@
+The Qt Quick module implements the "standard library" for QML
+
+The Qt Quick module is the standard library for writing QML applications. While
+the Qt Qml module provides the QML engine and language infrastructure, the Qt
+Quick module provides all the basic types necessary for creating user
+interfaces with QML. It provides a visual canvas and includes types for
+creating and animating visual components, receiving user input, creating data
+models and views and delayed object instantiation.
+
+The Qt Quick module provides both a QML API, which supplies QML types for
+creating user interfaces with the QML language, and a Python API for extending
+QML applications with Python code.
+
+.. note:: A set of Qt Quick-based UI controls is also available to create user interfaces.
+          See :mod:`PySide6.QtQuickControls2` for more information.
+
+If you're new to QML and Qt Quick, please see :ref:`QML-Applications` for an
+introduction to writing QML applications.
+
+Using the Module
+^^^^^^^^^^^^^^^^
+
+To include the definitions of modules classes, use the following
+directive:
+
+::
+
+    import PySide6.QtQuick
+
+Important Concepts in Qt Quick
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Qt Quick provides everything you need to create a rich application with a fluid
+and dynamic user interface. It enables you to build user interfaces around the
+behavior of user interface components and how they connect with one another,
+and it provides a visual canvas with its own coordinate system and rendering
+engine. Animation and transition effects are first class concepts in Qt Quick,
+and you can add visual effects through specialized components for particle and
+shader effects.
+
+    * :ref:`Important-Concepts-In-Qt-Quick---The-Visual-Canvas`
+    * :ref:`Important-Concepts-In-Qt-Quick---User-Input`
+    * :ref:`Important-Concepts-In-Qt-Quick---Positioning`
+    * :ref:`Important-Concepts-in-Qt-Quick---States--Transitions-and-Animations`
+    * :ref:`Important-Concepts-In-Qt-Quick---Data---Models--Views-and-Data-Storage`
+    * :ref:`Important-Concepts-In-Qt-Quick---Graphical-Effects`
+    * `MultiEffect <https://doc.qt.io/qt-6/qml-qtquick-effects-multieffect.html>`_
+    * :ref:`Important-Concepts-In-Qt-Quick---Convenience-Types`
+
+When using the Qt Quick module, you will need to know how to write QML
+applications using the QML language. In particular, QML Basics and QML
+Essentials from the QML Applications page.
+
+To find out more about using the QML language, see :mod:`Qt Qml <PySide6.QtQml>`.
+
+C++ Extension Points
+^^^^^^^^^^^^^^^^^^^^
+
+    * :ref:`C++ Extension Points<C---Extension-Points-Provided-By-Qt-Quick>`
+
+Articles and Guides
+^^^^^^^^^^^^^^^^^^^
+
+    * :ref:`Qt Quick Guidelines<Best-Practices-for-QML-and-Qt-Quick>`
+    * :ref:`Qt Quick Tools and Utilities<Qt-Quick-Tools-and-Utilities>`
+
+Further information for writing QML applications:
+
+    * :ref:`QML-Applications` - Essential information for application development with QML and Qt Quick
+    * :mod:`Qt Qml <PySide6.QtQml>` - Documentation for the Qt QML module, which provides the QML engine and language infrastructure
+    * :ref:`Qt Quick How-tos<Qt-Quick-How-tos>` - shows how to achieve specific tasks in Qt Quick
+
+Qt Academy Courses
+^^^^^^^^^^^^^^^^^^
+
+    * `Introduction to Qt Quick <https://www.qt.io/academy/course-catalog#introduction-to-qt-quick>`_
+
+List of Classes by Function
+---------------------------
+
+    * :ref:`Qt-Quick-Scene-Graph-Material-Classes`
+    * :ref:`Qt-Quick-Scene-Graph-Node-classes`
index dba542045bc736af346c91d339e3e7df1dddb21d..dcd5ea1ba1f7ed7c8aea9b19c982af21bcb1980c 100644 (file)
@@ -3,7 +3,7 @@ Provides a set of UI controls for Qt Quick.
 Qt Quick Controls provides a set of controls that can be used to build complete
 interfaces in Qt Quick. The module was introduced in Qt 5.7.
 
-    .. image:: images/qtquickcontrols2-styles.png
+    .. image:: images/qtquickcontrols-styles.png
 
 Qt Quick Controls comes with a selection customizable styles. See
 :ref:`Styling Qt Quick Controls<Styling-Qt-Quick-Controls>` for more details.
index 52f13590b1198854532dec8cbe79f958cbd71c2f..fefd51f5c6eda5265870fd95d10ff03abedb37f7 100644 (file)
@@ -24,8 +24,7 @@ executed.
 QML API
 ^^^^^^^
 
-The `QML types <https://doc.qt.io/qt-6/qttest-qmlmodule.html>`_
-in Qt Quick Test are available through the ``QtTest`` import.
+The QML types in Qt Quick Test are available through the ``QtTest`` import.
 To use the types, add the following import statement to your ``.qml`` file:
 
 .. code-block:: JavaScript
index 03a4d5477a65a0665155c79c25483326bbdae5fd..3355d1a4e6c9acd0b5c808881b632d9d033b6349 100644 (file)
@@ -15,5 +15,3 @@ directive:
 ::
 
     import PySide6.QtSensors
-
-The module also provides `QML types <https://doc.qt.io/qt-6/qtsensors-qmlmodule.html>`_ .
index f3374edc85691049897f4e70537feb976d10fc39..07673f294b12f615758856c0d2f90e0c4f04798b 100644 (file)
@@ -19,3 +19,8 @@ directive:
 ::
 
     import PySide6.QtSql
+
+List of Classes by Function
+---------------------------
+
+    * :ref:`Database-Classes`
index 5207a34861ab4d461648266c6542d8474fdf3e7e..5e469d5136233c1a9833b32a1ba0e24ff33d64c7 100644 (file)
@@ -26,5 +26,3 @@ API Reference
 ^^^^^^^^^^^^^
 
     * `JavaScript API <https://doc.qt.io/qt-6/qtwebchannel-javascript.html>`_
-
-The module also provides `QML types <https://doc.qt.io/qt-6/qtwebchannel-qmlmodule.html>`_ .
index f15264699f9210c23976907ea03de41c8f6e2cac..ec81f738fc90d83ff202133c3de8599593344f30 100644 (file)
@@ -20,8 +20,6 @@ directive:
 
     import PySide6.QtWebSockets
 
-The module also provides `QML types <https://doc.qt.io/qt-6/qtwebsockets-qmlmodule.html>`_ .
-
 Articles and Guides
 ^^^^^^^^^^^^^^^^^^^
 
diff --git a/sources/pyside6/doc/extras/QtWebView.rst b/sources/pyside6/doc/extras/QtWebView.rst
new file mode 100644 (file)
index 0000000..1b029ac
--- /dev/null
@@ -0,0 +1,25 @@
+
+Qt WebView lets you display web content inside a QML application. To avoid including a full web
+browser stack, Qt WebView uses native APIs where appropriate.
+
+Getting Started
+^^^^^^^^^^^^^^^
+
+To include the definitions of modules classes, use the following
+directive:
+
+    ::
+
+        from PySide6.QtWebView import QtWebView
+
+To make the Qt WebView module function correctly across all platforms, it's
+necessary to call ``QtWebView.initialize()`` before creating the QGuiApplication
+instance and before window's QPlatformOpenGLContext is created. For usage,
+see the ``minibrowser`` example in the PySide6 examples package.
+
+API Reference
+^^^^^^^^^^^^^
+
+    * `Qt API <https://doc.qt.io/qt-6/qtwebview-index.html>`_
+
+The module also provides `QML types <https://doc.qt.io/qt-6/qtwebview-index.html#qml-api>`_
index 784421aac4f70784b3feb01db33dda7ede8aaadf..62d934b08ea3f76de3100c2532e035d458076d28 100644 (file)
@@ -52,7 +52,7 @@ widget reports its size requirements to the layout through the
 :meth:`sizePolicy<PySide6.QtWidgets.QWidget.sizePolicy>` properties,
 and the layout distributes the available space accordingly.
 
-:ref:`Qt Widgets Designer<using_ui_files>` is a powerful tool for interactively
+:ref:`Qt Widgets Designer<tutorial_uifiles>` is a powerful tool for interactively
 creating and arranging widgets in layouts.
 
 Model/View Classes
@@ -63,7 +63,7 @@ classes that manage the way data is presented to the user. Data-driven
 applications which use lists and tables are structured to separate the
 data and view using models, views, and delegates.
 
-    .. image:: images/windows-treeview.png
+    .. image:: images/fusion-treeview.png
 
 Graphics View
 ^^^^^^^^^^^^^
@@ -84,3 +84,8 @@ directive:
 ::
 
     import PySide6.QtWidgets
+
+List of Classes by Function
+---------------------------
+
+    * :ref:`Widgets-Classes`
diff --git a/sources/pyside6/doc/faq/porting_from2.rst b/sources/pyside6/doc/faq/porting_from2.rst
new file mode 100644 (file)
index 0000000..33f1a42
--- /dev/null
@@ -0,0 +1,96 @@
+.. _faq-porting-from2:
+
+Porting Applications from PySide2 to PySide6
+============================================
+
+Module Availability
+-------------------
+
+Qt for Python 6.2.0 provides all modules planned for inclusion in Qt 6.
+
+Module-Level Changes
+--------------------
+
+* The modules *QtMacExtras*, *Qt Quick Controls 1*, *QtWinExtras*,
+  *QtXmlPatterns* and *QtX11Extras* have been removed.
+* ``QStateMachine`` and related classes have been extracted to a new
+  *QtStateMachine* module.
+* The modules *QtWebKit* and *QtWebKitWidgets* have been replaced by the new
+  *QtWebEngineCore*, *QtWebEngineQuick* and *QtWebEngineWidgets* modules.
+* ``QXmlReader`` and related classes (*SAX API*) have been removed.
+* The content of the *QtOpenGL* module has been replaced. The class
+  ``QGLWidget`` and related classes (``QGLContext``, ``QGLFunctions``,
+  ``QGLShaderProgram``) have been removed. Parts of the *Open GL*
+  functionality from *QtGui* have been extracted into this module, for example
+  ``QOpenGLBuffer`` and ``QOpenGLShaderProgram``.
+  There is a new module *QtOpenGLWidgets* which contains the class
+  ``QOpenGLWidget``, a replacement for ``QGLWidget``.
+
+As *Open GL*  is phasing out,
+`QRhi <https://doc.qt.io/qt-6/topics-graphics.html>`_ should be considered
+for graphics applications.
+
+Imports
+-------
+
+The first thing to do when porting applications is to replace the
+import statements:
+
+.. code-block:: python
+
+    from PySide2.QtWidgets import QApplication
+    from PySide2 import QtCore
+
+needs to be changed to:
+
+.. code-block:: python
+
+    from PySide6.QtWidgets import QApplication
+    from PySide6 import QtCore
+
+
+Some classes are in a different module now, for example
+``QAction`` and ``QShortcut`` have been moved from ``QtWidgets`` to ``QtGui``.
+
+For *Qt Charts* and *Qt Data Visualization*, the additional namespaces have been
+removed. It is now possible to use:
+
+.. code-block:: python
+
+    from PySide6.QtCharts import QChartView
+
+directly.
+
+
+Class/Function Deprecations
+---------------------------
+
+Then, the code base needs to be checked for usage of deprecated API and adapted
+accordingly. For example:
+
+* The High DPI scaling attributes ``Qt.AA_EnableHighDpiScaling``,
+  ``Qt.AA_DisableHighDpiScaling`` and ``Qt.AA_UseHighDpiPixmaps`` are
+  deprecated. High DPI is by default enabled in Qt 6 and cannot be turned off.
+* ``QDesktopWidget`` has been removed. ``QScreen`` should be used instead,
+  which can be retrieved using ``QWidget.screen()``,
+  ``QGuiApplication.primaryScreen()`` or ``QGuiApplication.screens()``.
+* ``QFontMetrics.width()`` has been renamed to ``horizontalAdvance()``.
+* ``QMouseEvent.pos()`` and ``QMouseEvent.globalPos()`` returning a ``QPoint``
+  as well as ``QMouseEvent.x()`` and ``QMouseEvent.y()`` returning ``int``
+  are now deprecated. ``QMouseEvent.position()`` and
+  ``QMouseEvent.globalPosition()`` returning a ``QPointF`` should be used
+  instead.
+* ``Qt.MidButton`` has been renamed to ``Qt.MiddleButton``.
+* ``QOpenGLVersionFunctionsFactory.get()`` instead of
+  ``QOpenGLContext.versionFunctions()`` should be used to obtain
+  *Open GL* functions.
+* ``QRegExp`` has been replaced by ``QRegularExpression``.
+* ``QWidget.mapToGlobal()`` and ``QWidget.mapFromGlobal()`` now also accept
+  and return ``QPointF``.
+* Functions named ``exec_`` (classes ``QCoreApplication``, ``QDialog``,
+  ``QEventLoop``) have been renamed to ``exec`` which became possible
+  in Python 3.
+
+More information can be found in the
+`Porting to Qt 6 <https://doc.qt.io/qt-6/portingguide.html>`_ Guide
+and the `Qt 6.2 Documentation <https://doc.qt.io/qt-6/index.html>`_ .
index 3155b24fc43da5b5b21125413c5f36366ffdb576..a93f2e9e99defc40e842a912d0eeee051424d87c 100644 (file)
@@ -64,7 +64,7 @@ The `pyside6-uic` tool generates Python code from these `.ui` files,
 which you can import from your main files, so it is not necessary
 for you to include the `.ui` files in your deployed application.
 
-For more details, see :ref:`using_ui_files`.
+For more details, see :ref:`tutorial_uifiles`.
 
 Resource Collection Files ``.qrc``
 ----------------------------------
@@ -86,7 +86,7 @@ The `pyside6-rcc` tool generates Python code from these `.qrc` files,
 so you are not required to include the listed files in your deployed
 application.
 
-For more details, see :ref:`using_qrc_files`.
+For more details, see :ref:`tutorial_qrcfiles`.
 
 Qt Modeling Language File ``.qml``
 ----------------------------------
index 3b42a9403163d467f46ece9308b7e81f246ca270..deb1f24ccfd2ceadd02bdddac8b9e512e5482594 100644 (file)
@@ -66,7 +66,7 @@ is not to affect the user experience compared to other included applications.
 .. note:: You can adapt these applications to use your self-made style, but
    you need to be aware that the goal of Widgets is to respect the system
    style, be careful when changing colors. Check this
-   :ref:`simple tutorial <widgetstyling>` on how to do so.
+   :ref:`simple tutorial <tutorial_widgetstyling>` on how to do so.
 
 QML
 ---
diff --git a/sources/pyside6/doc/gettingstarted.rst b/sources/pyside6/doc/gettingstarted.rst
new file mode 100644 (file)
index 0000000..9abe645
--- /dev/null
@@ -0,0 +1,341 @@
+.. _getting-started:
+
+Getting Started
+===============
+
+Here you can find the steps to install and create a simple application
+using the two technologies that Qt provides: Qt Widgets and Qt Quick.
+
+.. note:: If you are new to Qt, you can check the :ref:`faq-section` section at
+   the end of this page to understand concepts, file types, compatibles IDEs,
+   etc.  In case you own a Qt License, please refer to :ref:`commercial-page`.
+
+Requirements
+------------
+
+Before you can install |project|, first you must install the following software:
+
+* `Official <https://www.python.org/downloads/>`_ Python 3.8+
+* We **highly** recommend using a virtual environment, such as
+  `venv <https://docs.python.org/3/library/venv.html>`_ or
+  `virtualenv <https://virtualenv.pypa.io/en/latest>`_
+  and avoid installing PySide6 via ``pip`` in your system.
+
+Installation
+------------
+
+* **Creating and activating an environment**
+  You can do this by running the following on a terminal:
+
+  * Create environment (Your Python executable might be called ``python3``)::
+
+        python -m venv env
+
+  * Activate the environment (Linux and macOS)::
+
+        source env/bin/activate
+
+  * Activate the environment (Windows)::
+
+        env\Scripts\activate.bat
+
+  Check this animation on how to do it:
+
+  .. image:: https://qt-wiki-uploads.s3.amazonaws.com/images/8/8a/Pyside6_install.gif
+     :alt: PySide6 Installation GIF
+
+  .. note:: Having Qt installed in your system will not interfere with your
+     PySide6 installation if you do it via ``pip install``, because the Python
+     packages (wheels) already includes Qt binaries. Most notably, style plugins
+     from the system won't have any effect on PySide applications.
+
+
+* **Installing PySide6**
+
+  .. note:: For a commercial installation, refer to :ref:`commercial-page`.
+
+  Now you are ready to install the |project| packages using ``pip``.
+  From the terminal, run the following command:
+
+  * For the latest version::
+
+        pip install pyside6
+
+  * For a specific version, like 6.4.1::
+
+        pip install pyside6==6.4.1
+
+  * It is also possible to install a specific snapshot from our servers.
+    To do so, you can use the following command::
+
+      pip install --index-url=https://download.qt.io/snapshots/ci/pyside/6.4/latest pyside6 --trusted-host download.qt.io
+
+* **Test your installation**
+
+  Now that you have |project| installed, test your setup by running the following Python
+  constructs to print version information::
+
+    import PySide6.QtCore
+
+    # Prints PySide6 version
+    print(PySide6.__version__)
+
+    # Prints the Qt version used to compile PySide6
+    print(PySide6.QtCore.__version__)
+
+  .. note:: For more information about what's included in the ``pyside6``
+     package, check :ref:`package_details`.
+
+
+Create your first Qt Application
+--------------------------------
+
+.. image:: https://qt-wiki-uploads.s3.amazonaws.com/images/e/eb/Pyside6_widgets_quick.gif
+   :alt: Qt Widgets and Qt Quick comparison header animation
+
+Qt provides two technologies to build User Interfaces:
+
+* Qt Widgets, an imperative programming and design approach that has been
+  around since the beginning of Qt, making it a stable and reliable technology
+  for UI applications.
+* Qt Quick, a declarative programming and design approach, which enables you to
+  create fluid UIs by describing them in terms of simple elements.
+
+Both technologies offer you the possibility to use *drag and drop* tools
+to create your interfaces. :ref:`pyside6-designer` for Qt Widgets (included
+when you install pyside6), and Qt Design Studio for Qt Quick (`Get it here`_).
+
+.. note:: After reading this page, it is recommended that you check the
+   :ref:`pyside6-project` tool to learn how to create projects automatically
+   without writing all the code by hand.
+
+.. _`Get it here`: https://doc.qt.io/qt-6/install-qt-design-studio.html
+
+Create your first Qt Application with Qt Widgets
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Your |project| setup is ready. You can explore it further by developing a simple application
+that prints ``"Hello World"`` in several languages. The following instructions will
+guide you through the development process:
+
+* **Imports**
+
+  Create a new file named :code:`hello_world.py`, and add the following imports to it.::
+
+    import sys
+    import random
+    from PySide6 import QtCore, QtWidgets, QtGui
+
+  The |pymodname| Python module provides access to the Qt APIs as its submodule.
+  In this case, you are importing the :ref:`QtCore`, :ref:`QtWidgets`, and :ref:`QtGui` submodules.
+
+* **Main Class**
+
+  Define a class named :code:`MyWidget`, which extends :ref:`QWidget` and
+  includes a :ref:`QPushButton` and :ref:`QLabel`.::
+
+    class MyWidget(QtWidgets.QWidget):
+        def __init__(self):
+            super().__init__()
+
+            self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет Ð¼Ð¸Ñ€"]
+
+            self.button = QtWidgets.QPushButton("Click me!")
+            self.text = QtWidgets.QLabel("Hello World",
+                                         alignment=QtCore.Qt.AlignCenter)
+
+            self.layout = QtWidgets.QVBoxLayout(self)
+            self.layout.addWidget(self.text)
+            self.layout.addWidget(self.button)
+
+            self.button.clicked.connect(self.magic)
+
+        @QtCore.Slot()
+        def magic(self):
+            self.text.setText(random.choice(self.hello))
+
+  The ``MyWidget`` class has the :code:`magic` member function that randomly
+  chooses an item from the :code:`hello` list. When you click the button, the
+  :code:`magic` function is called.
+
+* **Application execution**
+
+  Now, add a main function where you instantiate :code:`MyWidget` and :code:`show` it.::
+
+    if __name__ == "__main__":
+        app = QtWidgets.QApplication([])
+
+        widget = MyWidget()
+        widget.resize(800, 600)
+        widget.show()
+
+        sys.exit(app.exec())
+
+  Run your example by writing the following command:
+
+  .. code-block:: bash
+
+    python hello_world.py
+
+  Try clicking the button at the bottom to see which greeting you get.
+
+  .. image:: images/screenshot_hello_widgets.png
+     :alt: Hello World application in Qt Widgets
+
+Create your first Qt Application with Qt Quick
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To do the same using Qt Quick:
+
+* **Imports**
+
+  Create a new file named :code:`hello_world_quick.py`, and add the following imports to it.::
+
+    import sys
+    from PySide6.QtGui import QGuiApplication
+    from PySide6.QtQml import QQmlApplicationEngine
+
+* **Declarative UI**
+
+  The UI can be described in the QML language:
+
+  .. code-block:: javascript
+
+    import QtQuick
+    import QtQuick.Controls
+    import QtQuick.Layouts
+
+    Window {
+        width: 300
+        height: 200
+        visible: true
+        title: "Hello World"
+
+        readonly property list<string> texts: ["Hallo Welt", "Hei maailma",
+                                               "Hola Mundo", "Привет Ð¼Ð¸Ñ€"]
+
+        function setText() {
+            var i = Math.round(Math.random() * 3)
+            text.text = texts[i]
+        }
+
+        ColumnLayout {
+            anchors.fill:  parent
+
+            Text {
+                id: text
+                text: "Hello World"
+                Layout.alignment: Qt.AlignHCenter
+            }
+            Button {
+                text: "Click me"
+                Layout.alignment: Qt.AlignHCenter
+                onClicked:  setText()
+            }
+        }
+    }
+
+  Put the this into a file named :code:`Main.qml` into a directory named
+  :code:`Example` along with a file named :code:`qmldir` to describe a basic
+  QML module:
+
+  .. code-block:: text
+
+    module Example
+    Main 254.0 Main.qml
+
+* **Application execution**
+
+  Now, add a main function where you instantiate a :ref:`QQmlApplicationEngine` and
+  load the QML::
+
+    import sys
+    from PySide6.QtGui import QGuiApplication
+    from PySide6.QtQml import QQmlApplicationEngine
+
+    if __name__ == "__main__":
+        app = QGuiApplication(sys.argv)
+        engine = QQmlApplicationEngine()
+        engine.addImportPath(sys.path[0])
+        engine.loadFromModule("Example", "Main")
+        if not engine.rootObjects():
+            sys.exit(-1)
+        exit_code = app.exec()
+        del engine
+        sys.exit(exit_code)
+
+  Run your example by writing the following command:
+
+  .. code-block:: bash
+
+    python main.py
+
+
+  Try clicking the button at the bottom to see which greeting you get.
+
+  .. image:: images/screenshot_hello_quick.png
+     :alt: Hello World application in Qt Quick
+
+Next steps
+----------
+
+Now that you have use both technologies, you can head to our
+:ref:`pyside6_examples` and :ref:`pyside6_tutorials` sections.
+
+.. _faq-section:
+
+Frequently Asked Questions
+--------------------------
+
+Here you can find a couple of common questions and situations that will
+clarify questions before you start programming.
+
+.. grid:: 1 3 3 3
+    :gutter: 2
+
+    .. grid-item-card:: What is Qt
+        :link: whatisqt
+        :link-type: ref
+
+        Qt, QML, Widgets... What is the difference?
+
+    .. grid-item-card:: Compatible IDEs
+        :link: whichide
+        :link-type: ref
+
+        Which IDEs are compatible with PySide?
+
+    .. grid-item-card:: Binding Generation
+        :link: whatisshiboken
+        :link-type: ref
+
+        What is Shiboken?
+
+    .. grid-item-card:: File types
+        :link: typesoffiles
+        :link-type: ref
+
+        File Types in PySide
+
+    .. grid-item-card:: App distribution
+        :link: distribution
+        :link-type: ref
+
+        Distributing your application to other systems and platforms
+
+    .. grid-item-card:: Why Qt for Python?
+        :link: whyqtforpython
+        :link-type: ref
+
+        As a Qt/C++ developer, why should I consider Qt for Python?
+
+.. toctree::
+    :hidden:
+
+    faq/whatisqt.rst
+    faq/whichide.rst
+    faq/whatisshiboken.rst
+    faq/typesoffiles.rst
+    faq/distribution.rst
+    faq/whyqtforpython.rst
+    faq/porting_from2.rst
diff --git a/sources/pyside6/doc/gettingstarted/index.rst b/sources/pyside6/doc/gettingstarted/index.rst
deleted file mode 100644 (file)
index 9d36061..0000000
+++ /dev/null
@@ -1,572 +0,0 @@
-Getting Started
-===============
-
-.. important:: This page is focused on building |project| **from source**.
-  If you just want to install |pymodname|, you need to run: :command:`pip install pyside6`.
-
-  For more details, refer to our :ref:`quick-start` guide. Additionally, you can check the
-  :ref:`FAQ <faq>` related to the project.
-
-.. _Quick Start: quickstart.html
-
-General Requirements
---------------------
-
-The following prerequisites must be installed before you build |project|.
-On **Linux** you might get them with your operating system package manager, on **macOS**
-you might get them with ``brew``, and on **Windows** you can download the installer from each
-website.
-
-* **Python**: 3.9+ `[official Python website] <https://www.python.org/downloads/>`_
-* **Qt:** 6.4+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
-* **CMake:** 3.18+ `[official CMake website] <https://cmake.org/download/>`_
-* **Git:** 2.0+. `[official Git website] <https://git-scm.com/downloads>`_
-* **libclang:** The libclang library, recommended: version 10 for 6.0+.
-  Prebuilt versions for each OS can be `downloaded here`_.
-* Check the `Supported Platforms of Qt`_
-
-.. _downloaded here: https://download.qt.io/development_releases/prebuilt/libclang/
-.. _`Supported Platforms of Qt` : https://doc.qt.io/qt-6/supported-platforms.html
-
-Guides per platform
--------------------
-
-.. toctree::
-    :maxdepth: 1
-    :hidden:
-
-    linux.rst
-    macOS.rst
-    windows.rst
-    package_details.rst
-    porting_from2.rst
-
-You can refer to the following pages for platform specific instructions:
-
-.. grid:: 1 3 3 3
-    :gutter: 2
-
-    .. grid-item-card::
-        :img-top: ../images/windows.svg
-        :class-item: text-center
-
-        +++
-        .. button-ref:: windows
-            :color: primary
-            :outline:
-            :expand:
-
-            Windows
-
-    .. grid-item-card::
-        :img-top: ../images/macos.svg
-        :class-item: text-center
-
-        +++
-        .. button-ref:: macOS
-            :color: primary
-            :outline:
-            :expand:
-
-            macOS
-
-    .. grid-item-card::
-        :img-top: ../images/linux.svg
-        :class-item: text-center
-
-        +++
-        .. button-ref:: linux
-            :color: primary
-            :outline:
-            :expand:
-
-            Linux
-
-.. important:: |project| does not yet support WebAssembly and the mobile operating systems (Android or iOS).
-               Most Linux-based embedded OS provide PySide with their official
-               package manager (for example, `Raspbian`_ and `ArchlinuxARM`_).
-
-.. _Raspbian: https://www.raspbian.org/
-.. _ArchlinuxARM: https://archlinuxarm.org/
-
-A normal building command will look like this::
-
-    python setup.py install --qtpaths=/path/to/qtpaths \
-                            --ignore-git \
-                            --debug \
-                            --build-tests \
-                            --parallel=8 \
-                            --verbose-build \
-                            --module-subset=Core,Gui,Widgets
-
-Which will build and install the project with **debug** symbols, including the **tests**,
-using **ninja** (instead of make), and considering only the **module subset** of
-:mod:`QtCore <PySide6.QtCore>`, :mod:`QtGui <PySide6.QtGui>`, and
-:mod:`QtWidgets <PySide6.QtWidgets>`.
-
-`CMake Unity Build Mode`_ is used by default for speed-up.
-
-Other important options to consider are:
- * ``--cmake``, to specify the path to the cmake binary,
- * ``--reuse-build``, to rebuild only the modified files,
- * ``--openssl=/path/to/openssl/bin``, to use a different path for OpenSSL,
- * ``--standalone``, to copy over the Qt libraries into the final package to make it work on other
-   machines,
- * ``--build-docs``, to enable the documentation build.
- * ``--doc-build-online``, to build documentation using the online template (by
-   default it uses the offline)
-
-Testing the installation
-------------------------
-
-Once the installation finishes, you will be able to execute any of our examples::
-
-  python examples/widgets/widgets/tetrix.py
-
-Running Tests
--------------
-
-Using the ``--build-tests`` option will enable us to run all the auto tests inside the project::
-
-  python testrunner.py test > testlog.txt
-
-.. note:: On Windows, don't forget to have qtpaths in your path
-   (:command:`set PATH=C:\\\Path\\\to\\\Qt\\\6.4.0\\\msvc2019_64\\\bin;%PATH%`)
-
-You can also run a specific test (for example ``qpainter_test``) by running::
-
-    ctest -R qpainter_test --verbose
-
-.. _cross_compilation:
-
-Cross Compilation
------------------
-
-Starting from 6.3, it is possible to cross-compile Shiboken (module), and
-PySide. This functionality is still in Technical Preview, which means it could
-change in the future releases.
-
-.. important:: The only supported configuration is using a host Linux
-   machine to cross-compile to a Linux target platform.
-
-Cross compiling software is a valid use case that many projects rely on,
-however, it is a complicated process that might fail due to many reasons.
-
-Before starting with the process, it is important to understand the details of
-the build system, and the goal of cross compilation.
-
-In the build process, a ``Host`` is the computer you are currently using to
-compile, and a ``Target`` is your embedded device that you are compiling for.
-
-Qt for Python is being built using setuptools, and relies on a ``setup.py`` file
-that is called recursively to build Shiboken (module),
-Shiboken (generator), and PySide. As the generator is creating
-the wrappers for the bindings, it's not cross compiled
-for the target.
-Only the Shiboken (module) and PySide are cross compiled.
-
-The building process requires a Qt installation, and a Python interpreter
-on both the host, and the target. The used Qt versions on both platforms
-should have the same minor version. That is, Qt 6.3 (host)
-cannot be used with a Qt 6.2 (target), or the other way around.
-
-.. note:: It is recommended to use the same version of Qt on both host and target to avoid possible
-  unconventional errors.
-
-Prerequisites
-~~~~~~~~~~~~~
-
-First and foremost, you need to have access to the target device because you
-need to copy several system files (sysroot). We recommend a Linux OS that has
-the latest Qt versions, like `Manjaro ARM`_ or `Archlinux ARM`_.
-
-* (target) Install Qt 6.3+ on the system using the package manager.
-* (host) Install Qt 6.3+ on the system using the package manager or Qt
-  Installer.
-* (target, host) Install the library and development packages that provide
-  C++ headers, linkers, libraries, and compilers.
-* (target, host) Install Python interpreter v3.7 or later
-* (target, host) Install CMake 3.17+
-
-After installing these prerequisites, copy the ``target`` sysroot to your
-``host`` computer. This process is tricky, because copying system files from
-another computer might cause problems with the symbolic links. Here you
-have two options to achieve that.
-
-Option A: Copying the files
-***************************
-
-Create a directory to copy the sysroot of your target device,
-for example ``rpi-sysroot``, and perform the copy on your host computer:
-
-.. code-block:: bash
-
-    rsync -vR --progress -rl --delete-after --safe-links \
-        USERNAME@TARGET_IP:/{lib,usr,opt/vc/lib} rpi-sysroot/
-
-Ensure to replace ``USERNAME`` and ``TARGET_IP`` with your system appropriate
-values.
-
-Option B: Packaging the file system
-***********************************
-
-Create a package for your sysroot in your target:
-
-.. code-block:: bash
-
-    tar cfJ ~/sysroot.tar.xz /lib /usr /opt/vc/lib
-
-Copy the package from the target to your host:
-
-.. code-block:: bash
-
-    rsync -vR --progress USERNAME@TARGET_IP:sysroot.tar.xz .
-
-Once you have the tar file, unpack it inside a ``rpi-sysroot`` directory.
-
-Fix Symlinks
-************
-
-It is recommended to run the following script to fix
-most of the issues you would find with symbolic links:
-
-.. code-block:: python
-
-    import sys
-    from pathlib import Path
-    import os
-
-    # Take a sysroot directory and turn all the absolute symlinks and turn them into
-    # relative ones such that the sysroot is usable within another system.
-
-    if len(sys.argv) != 2:
-        print(f"Usage is {sys.argv[0]} <sysroot-directory>")
-        sys.exit(-1)
-
-    topdir = Path(sys.argv[1]).absolute()
-
-    def handlelink(filep, subdir):
-        link = filep.readlink()
-        if str(link)[0] != "/":
-            return
-        if link.startswith(topdir):
-            return
-        relpath = os.path.relpath((topdir / link).resolve(), subdir)
-        os.unlink(filep)
-        os.symlink(relpath, filep)
-
-    for f in topdir.glob("**/*"):
-        if f.is_file() and f.is_symlink():
-            handlelink(f, f.parent)
-
-Setting up the toolchain
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-To perform the cross compilation, you need a special set of compilers,
-libraries, and headers, which runs on the host architecture, but generates
-(binaries/executables) for a target architecture.
-For example, from x86_64 to aarch64.
-
-It is recommended to use the official 10.2 `ARM Developer cross compilers`_,
-which you can find on their official website. For this tutorial, we choose
-``aarch64`` target architecture and we will assume that you downloaded the
-`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`_ file,
-and unpacked it.
-
-With those compilers, now you need a CMake toolchain file. This is
-a configuration file to set the compilers and sysroot information, together
-with extra options like compilation flags, and other details. You can use the
-following file as an example, but keep in mind they might vary:
-
-.. code-block:: cmake
-
-    # toolchain-aarch64.cmake
-    cmake_minimum_required(VERSION 3.18)
-    include_guard(GLOBAL)
-
-    set(CMAKE_SYSTEM_NAME Linux)
-    set(CMAKE_SYSTEM_PROCESSOR aarch64)
-
-    set(TARGET_SYSROOT /path/to/your/target/sysroot)
-    set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
-
-    set(CMAKE_SYSROOT ${TARGET_SYSROOT})
-
-    set(ENV{PKG_CONFIG_PATH} "")
-    set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
-    set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
-
-    set(CMAKE_C_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-gcc)
-    set(CMAKE_CXX_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-g++)
-
-    set(QT_COMPILER_FLAGS "-march=armv8-a")
-    set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
-    set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")
-
-    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
-
-    include(CMakeInitializeConfigs)
-
-    function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING)
-      if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS")
-        set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}")
-
-        foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
-          if (DEFINED QT_COMPILER_FLAGS_${config})
-            set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}")
-          endif()
-        endforeach()
-      endif()
-
-      if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS")
-        foreach (config SHARED MODULE EXE)
-          set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}")
-        endforeach()
-      endif()
-
-      _cmake_initialize_per_config_variable(${ARGV})
-    endfunction()
-
-You need to adjust the paths in these two lines::
-
-    set(TARGET_SYSROOT /path/to/your/target/sysroot)
-    set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
-
-and replace them with the sysroot directory (the one we called ``rpi-sysroot``),
-and the compilers (the ``gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin`` directory).
-
-
-Cross compiling PySide
-~~~~~~~~~~~~~~~~~~~~~~
-
-After you have installed the prerequisites and copied the necessary files, you
-should have the following:
-
-* The compilers to cross compile (``gcc-argm-10.2-...``),
-* The target sysroot (``rpi-sysroot``),
-* The toolchain cmake file (``toolchain-aarch64.cmake``),
-* The ``pyside-setup`` repository,
-
-An example of the ``setup.py`` invocation might look like the following:
-
-.. code-block:: bash
-
-    python setup.py bdist_wheel \
-        --parallel=8 --ignore-git --reuse-build --standalone --limited-api=yes \
-        --cmake-toolchain-file=/opt/toolchain-aarch64.cmake \
-        --qt-host-path=/opt/Qt/6.3.0/gcc_64 \
-        --plat-name=linux_aarch64
-
-Depending on the target platform, you could use ``linux_armv7``,
-``linux_aarch64``, etc.
-
-For embedded platforms, which typically do not have Qt and its tools fully
-built, the option ``--no-qt-tools`` can be used to prevent the bundling of the
-tools.
-
-If the process succeeds, you will find the target wheels in your ``dist/``
-directory, for example:
-
-.. code-block:: bash
-
-    PySide6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
-    shiboken6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
-
-
-Troubleshooting
-***************
-
-* If the auto-detection mechanism fails to find the Python or Qt installations
-  you have in your target device, you can use two additional options::
-
-      --python-target-path=...
-
-  and::
-
-      --qt-target-path=...
-
-* In case the automatic build of the host Shiboken (generator) fails,
-  you can specify the custom path using::
-
-      --shiboken-host-path=...
-
-.. _`Manjaro ARM`: https://manjaro.org/download/#ARM
-.. _`Archlinux ARM`: https://archlinuxarm.org
-.. _`ARM Developer Cross Compilers`: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads
-.. _`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`: https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz
-
-.. _building_documentation:
-
-Building the documentation
---------------------------
-
-Starting from 6.3, the documentation is not being built by default.
-When using the second option described in this section, make sure to use the
-``--build-docs`` option.
-
-Install the specific documentation requirements in your Python virtual
-environment::
-
-    pip install -r requirements-doc.txt
-
-You can find the ``requirements-doc.txt`` file in the root of the repository.
-
-Starting from 5.15, there are two options to build the documentation:
-
-1. Building the base documentation (no API)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The process of parsing Qt headers to generate the PySide API documentation can take several
-minutes, this means that modifying a specific section of the documentation we currently have, might
-become a hard task. You may only care about the base documentation, which comprises all the
-documentation except for the API documentation.
-
-To generate this, execute the following command::
-
-    python setup.py build_base_docs
-
-This will generate an ``html/`` directory with the following structure::
-
-    html
-    â””── pyside6
-        â”œâ”€â”€ index.html
-        â”œâ”€â”€ ...
-        â””── shiboken6
-            â”œâ”€â”€ index.html
-            â””── ...
-
-so you can open the main page ``html/pyside6/index.html`` on your browser to check the generated
-files.
-
-This is useful when updating the general sections of the documentation, adding tutorials,
-modifying the build instructions, and more.
-
-.. note:: In case you are interested in only generating the Example Gallery,
-          you would need to run ``python tools/example_gallery/main.py`` to
-          generate the examples ``documentation`` for the gallery. This will
-          also be used internally by the ``build_base_docs`` target
-
-
-2. Building the documentation (Base + API)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The documentation is being generated using **qdoc** to get the API information, and also **sphinx**
-for the local Python related notes.
-
-The system requires the ``libxml2`` and ``libxslt`` libraries.
-
-After installing ``graphviz`` (included in the ``requirements-doc.txt`` file),
-the ``dot`` command needs to be in PATH, otherwise,
-the process will fail. Installing ``graphviz`` system-wide is also an option.
-
-Since the process relies on a Qt installation, you need to specify where the
-``qtbase`` directory of a Qt source tree is located by passing it to
-the command line option ``--qt-src-dir``.
-
-Once the common ``setup.py`` build process finishes (remember to use
-``--build-docs`` to enable the documentation build, and ``--doc-build-online``
-to get the HTML files), you can go to the generated
-``build/<your_env_name>/build/pyside6`` directory, and run::
-
-    ninja apidoc
-
-You can add ``-j X``, to perform the build process in parallel with
-X processes.
-
-.. note:: The :command:`apidoc` make target builds offline documentation in ``QCH``
-   (Qt Compressed Help) format by default. You can switch to building for the
-   online use with the ``--doc-build-online`` configure option.
-
-The target executes several steps:
-
-#. The ``qdoc`` tool is run over the Qt source code to produce documentation in WebXML format.
-#. ``shiboken6`` is run to extract the functions for which bindings exist from WebXML and convert it into RST.
-#. ``sphinx`` is run to produce the documentation in HTML format.
-
-Re-running the command will not execute step 1 unless the file
-``qdoc-output/webxml/qtcore-index.webxml`` is removed from the build tree.
-Similarly, step 2 will not be executed unless the file ``base/PySide6/QtCore/index.rst``
-is removed.
-
-Finally, you will get a ``html`` directory containing all the generated documentation. The offline
-help files, ``PySide.qch`` and ``Shiboken.qch``, can be moved to any directory of your choice. You
-can find ``Shiboken.qch`` in the build directory, ``build/<your_env_name>/build/shiboken6/doc/html``.
-
-If you want to temporarily change a ``.rst`` file to examine the impact on
-formatting, you can re-run ``sphinx`` in the ``doc`` directory::
-
-    sphinx-build base html
-
-Viewing offline documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The offline documentation (QCH) can be viewed using the *Qt Creator* IDE or
-*Qt Assistant*, which is a standalone application for viewing QCH files.
-
-To view the QCH using *Qt Creator*, following the instructions outlined in
-`Using Qt Creator Help Mode <https://doc.qt.io/qtcreator/creator-help.html>`_.
-If you chose to use *Qt Assistant* instead, use the following command to register
-the QCH file before launching *Qt Assistant*::
-
-    assistant -register PySide.qch
-
-Troubleshooting documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The documentation uses intersphinx to link from the PySide to the
-Shiboken documentation. This can fail if
-
-* the default ``QCH`` format is used; in which case the required ``objects.inv``
-  files are not generated. Use ``--doc-build-online``.
-* base and full doc builds are mixed, resulting in wrong values for the
-  intersphinx location in the CMake files. Re-run ``cmake`` to fix this.
-
-Using the internal tools
-------------------------
-
-A set of tools can be found under the ``tools/`` directory inside the ``pyside-setup`` repository.
-
-* ``checklibs.py``: Script to analyze dynamic library dependencies of Mach-O binaries.
-  To use this utility, just run::
-
-    python checklibs.py /path/to/some.app/Contents/MacOS/Some
-
-  This script was fetched from this repository_.
-
-* ``create_changelog.py``: Script used to create the CHANGELOG that you can find in the ``dist/``
-  directory. Usage::
-
-    python create_changelog.py -r 6.0.1 -v v6.0.0..6.0 -t bug-fix
-
-* ``debug_windows.py``: This script can be used to find out why PySide modules
-  fail to load with various DLL errors like Missing DLL or Missing symbol in DLL.
-
-  You can think of it as a Windows version of :command:`ldd` / ``LD_DEBUG``.
-
-  Underneath, it uses the :command:`cdb.exe` command line debugger and the :command:`gflags.exe`
-  tool, which are installed with the latest Windows Kit.
-
-  The aim is to help developers debug issues that they may encounter using the PySide imports on
-  Windows. The user should then provide the generated log file.
-
-  Incidentally it can also be used for any Windows executables, not just Python.
-  To use it just run::
-
-    python debug_windows.py
-
-* ``missing_bindings.py``: This script is used to compare the state of PySide and PyQt
-  regarding available modules and classses. This content is displayed in our `wiki page`_,
-  and can be used as follows::
-
-    python missing_bindings.py --qt-version 6.0.1 -w all
-
-.. note:: The script relies on BeautifulSoup to parse the content and generate a list of the
-   missing bindings.
-
-.. _repository: https://github.com/liyanage/macosx-shell-scripts/
-.. _`wiki page`: https://wiki.qt.io/Qt_for_Python_Missing_Bindings
-.. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/
-.. _`CMake Unity Build Mode` : https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.html
diff --git a/sources/pyside6/doc/gettingstarted/linux.rst b/sources/pyside6/doc/gettingstarted/linux.rst
deleted file mode 100644 (file)
index 912105e..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-Getting Started on Linux
-==========================
-
-Requirements
-------------
-
-* GCC
-* ``sphinx`` package for the documentation (optional).
-* Depending on your linux distribution, the following dependencies might also be required:
-
-  * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
-* Check the platform dependencies of `Qt for Linux/X11`_.
-
-Building from source
---------------------
-
-Creating a virtual environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
-which arbitrary modules can be installed and which can be removed after use::
-
-    python -m venv testenv
-    source testenv/bin/activate
-
-will create and use a new virtual environment, which is indicated by the command prompt changing.
-
-Setting up CLANG
-~~~~~~~~~~~~~~~~
-
-If you don't have libclang already in your system, you can download from the Qt servers::
-
-    wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
-
-Extract the files, and leave it on any desired path, and set the environment
-variable required::
-
-    7z x libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
-    export LLVM_INSTALL_DIR=$PWD/libclang
-
-Getting the source
-~~~~~~~~~~~~~~~~~~
-
-Cloning the official repository can be done by::
-
-    git clone https://code.qt.io/pyside/pyside-setup
-
-Checking out the version that we want to build, for example 6.5::
-
-    cd pyside-setup && git checkout 6.5
-
-Install the general dependencies::
-
-    pip install -r requirements.txt
-
-.. note:: Keep in mind you need to use the same version as your Qt installation.
-          Additionally, :command:`git checkout -b 6.5 --track origin/6.5` could be a better option
-          in case you want to work on it.
-
-Building and Installing (setuptools)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``setuptools`` approach uses the ``setup.py`` file to execute the build,
-install, and packaging steps.
-
-Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, :command:`/opt/Qt/6.5.0/gcc_64/bin/qtpaths`.
-
-Build can take a few minutes, so it is recommended to use more than one CPU core::
-
-    python setup.py build --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-To install on the current directory, just run::
-
-    python setup.py install --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-Building and Installing (cmake)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``setuptools`` approach includes internal ``CMake`` calls when
-building and installing the project, but a CMake-only approach is only
-recommended for packaging the project for distribution builds.
-
-Assumming that Qt is in PATH, for example, the configure step can be done with::
-
-    cmake -B /path/to/the/build/directory \
-          -S /path/to/the/pyside-setup \
-          -DCMAKE_INSTALL_PREFIX=/where/to/install \
-          -DPython_EXECUTABLE=/path/to/interpreter
-
-.. note:: You can add `-DFORCE_LIMITED_API=yes` in case you want to have a
-   build which will be compatible with Python 3.7+.
-
-and then for building::
-
-    cmake --build /path/to/the/build/directory --parallel X
-
-where `X` is the amount of processes you want to use.
-Finally, the install step can be done with::
-
-    cmake --install /path/to/the/build/directory
-
-.. note:: You can build only pyside6 or only shiboken6 by using
-   the diferent source directories with the option `-S`.
-
-
-Test installation
-~~~~~~~~~~~~~~~~~
-
-You can execute one of the examples to verify the process is properly working.
-Remember to properly set the environment variables for Qt and PySide::
-
-    python examples/widgets/widgets/tetrix.py
-
-.. _`Qt for Linux/X11`: https://doc.qt.io/qt-6/linux.html
diff --git a/sources/pyside6/doc/gettingstarted/macOS.rst b/sources/pyside6/doc/gettingstarted/macOS.rst
deleted file mode 100644 (file)
index ff457a4..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-Getting Started on macOS
-========================
-
-Requirements
-------------
-
-* `XCode`_ 8.2 (macOS 10.11), 8.3.3 (macOS 10.12), 9 (macOS 10.13), 10.1 (macOS 10.14)
-* ``sphinx`` package for the documentation (optional).
-* Depending on your OS, the following dependencies might also be required:
-
-  * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
-
-* Check the platform dependencies of `Qt for macOS`_.
-
-.. _XCode: https://developer.apple.com/xcode/
-.. _`Qt for macOS`: https://doc.qt.io/qt-6/macos.html
-
-Building from source
---------------------
-
-Creating a virtual environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
-which arbitrary modules can be installed and which can be removed after use::
-
-    python -m venv testenv  # your interpreter could be called 'python3'
-    source testenv/bin/activate
-
-will create and use a new virtual environment, which is indicated by the command prompt changing.
-
-Setting up CLANG
-~~~~~~~~~~~~~~~~
-
-If you don't have libclang already in your system, you can download from the Qt servers::
-
-    wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-macos-universal.7z
-
-Extract the files, and leave it on any desired path, and set the environment
-variable required::
-
-    7z x libclang-release_140-based-macos-universal.7z
-    export LLVM_INSTALL_DIR=$PWD/libclang
-
-Getting PySide
-~~~~~~~~~~~~~~
-
-Cloning the official repository can be done by::
-
-    git clone https://code.qt.io/pyside/pyside-setup
-
-Checking out the version that we want to build, for example, 6.5::
-
-    cd pyside-setup && git checkout 6.5
-
-Install the general dependencies::
-
-    pip install -r requirements.txt
-
-.. note:: Keep in mind you need to use the same version as your Qt installation
-
-Building PySide
-~~~~~~~~~~~~~~~
-
-Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``/opt/Qt/6.5.0/gcc_64/bin/qtpaths``.
-
-Build can take a few minutes, so it is recommended to use more than one CPU core::
-
-    python setup.py build --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-Installing PySide
-~~~~~~~~~~~~~~~~~
-
-To install on the current directory, just run::
-
-    python setup.py install --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-Test installation
-~~~~~~~~~~~~~~~~~
-
-You can execute one of the examples to verify the process is properly working.
-Remember to properly set the environment variables for Qt and PySide::
-
-    python examples/widgets/widgets/tetrix.py
diff --git a/sources/pyside6/doc/gettingstarted/package_details.rst b/sources/pyside6/doc/gettingstarted/package_details.rst
deleted file mode 100644 (file)
index 113edb6..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-.. _package_details:
-
-Package Details
-===============
-
-Having a large project as the Qt Framework available from one simple
-installation line::
-
-    pip install pyside6
-
-is really beneficial,
-but it might be confusing to newcomers.
-
-Besides your IDE, you don't need to install anything else to develop your
-Qt application, because the same command installs many tools
-that will help you design UIs, use QML types, generate
-files automatically, translate applications, etc.
-
-Package Dependencies
---------------------
-
-.. image:: packages.png
-   :width: 400
-   :alt: Packages structure and dependency
-
-Starting from 6.3.0, the ``pyside6`` package (wheel) is almost empty,
-and only includes references to other packages that are required
-to properly use all the modules.
-This packages are:
-
-* ``pyside6-essentials``, `essential Qt modules <https://pypi.org/project/PySide6-Essentials/>`_,
-* ``pyside6-addons``, `additional Qt modules <https://pypi.org/project/PySide6-Addons/>`_,
-* ``shiboken6``, a utility Python module.
-
-You can verify this by running ``pip list`` to check the installed
-packages in your Python (virtual) environment::
-
-  (env) % pip list
-  Package            Version
-  ------------------ -------
-  pip                22.0.4
-  PySide6            6.3.0
-  PySide6-Addons     6.3.0
-  PySide6-Essentials 6.3.0
-  setuptools         58.1.0
-  shiboken6          6.3.0
-
-Both ``pyside6-essentials`` and ``pyside6-addons`` contain Qt binaries
-(``.so``, ``.dll``, or ``.dylib``) that are used by the Python wrappers
-that enable you to use the Qt modules from Python.
-For example, in the ``QtCore`` module, you will find
-on Linux:
-
-* ``PySide6/QtCore.abi3.so``, and
-* ``PySide6/Qt/lib/libQt6Core.so.6``
-
-inside the ``site-packages`` directory of your (virtual) environment.
-The first is the *importable* module which depends on the second file
-which is the original QtCore library.
-
-.. note:: The package ``shiboken6-generator`` is not a dependency,
-   and it's not available on PyPi. The reason, is that it depends on
-   ``libclang``, which is a large library that we don't package, and
-   requires a special configuration for you to use. Check the `Shiboken
-   Documentation`_ for more details.
-
-..
-  Adding the full URL because it's a different sphinx project.
-.. _`Shiboken Documentation`: https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html
-
-Tools Included
---------------
-
-PySide6 comes bundled with a set of tools that assist in making the development experience with
-PySide6 more efficient. The list of tools can be found :ref:`here <package_tools>`.
diff --git a/sources/pyside6/doc/gettingstarted/packages.png b/sources/pyside6/doc/gettingstarted/packages.png
deleted file mode 100644 (file)
index 57e7ca4..0000000
Binary files a/sources/pyside6/doc/gettingstarted/packages.png and /dev/null differ
diff --git a/sources/pyside6/doc/gettingstarted/porting_from2.rst b/sources/pyside6/doc/gettingstarted/porting_from2.rst
deleted file mode 100644 (file)
index 6699f54..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-Porting Applications from PySide2 to PySide6
-============================================
-
-Module Availability
--------------------
-
-Qt for Python 6.2.0 provides all modules planned for inclusion in Qt 6.
-
-Module-Level Changes
---------------------
-
-* The modules *QtMacExtras*, *Qt Quick Controls 1*, *QtWinExtras*,
-  *QtXmlPatterns* and *QtX11Extras* have been removed.
-* ``QStateMachine`` and related classes have been extracted to a new
-  *QtStateMachine* module.
-* The modules *QtWebKit* and *QtWebKitWidgets* have been replaced by the new
-  *QtWebEngineCore*, *QtWebEngineQuick* and *QtWebEngineWidgets* modules.
-* ``QXmlReader`` and related classes (*SAX API*) have been removed.
-* The content of the *QtOpenGL* module has been replaced. The class
-  ``QGLWidget`` and related classes (``QGLContext``, ``QGLFunctions``,
-  ``QGLShaderProgram``) have been removed. Parts of the *Open GL*
-  functionality from *QtGui* have been extracted into this module, for example
-  ``QOpenGLBuffer`` and ``QOpenGLShaderProgram``.
-  There is a new module *QtOpenGLWidgets* which contains the class
-  ``QOpenGLWidget``, a replacement for ``QGLWidget``.
-
-As *Open GL*  is phasing out,
-`QRhi <https://doc.qt.io/qt-6/topics-graphics.html>`_ should be considered
-for graphics applications.
-
-Imports
--------
-
-The first thing to do when porting applications is to replace the
-import statements:
-
-.. code-block:: python
-
-    from PySide2.QtWidgets import QApplication
-    from PySide2 import QtCore
-
-needs to be changed to:
-
-.. code-block:: python
-
-    from PySide6.QtWidgets import QApplication
-    from PySide6 import QtCore
-
-
-Some classes are in a different module now, for example
-``QAction`` and ``QShortcut`` have been moved from ``QtWidgets`` to ``QtGui``.
-
-For *Qt Charts* and *Qt Data Visualization*, the additional namespaces have been
-removed. It is now possible to use:
-
-.. code-block:: python
-
-    from PySide6.QtCharts import QChartView
-
-directly.
-
-
-Class/Function Deprecations
----------------------------
-
-Then, the code base needs to be checked for usage of deprecated API and adapted
-accordingly. For example:
-
-* The High DPI scaling attributes ``Qt.AA_EnableHighDpiScaling``,
-  ``Qt.AA_DisableHighDpiScaling`` and ``Qt.AA_UseHighDpiPixmaps`` are
-  deprecated. High DPI is by default enabled in Qt 6 and cannot be turned off.
-* ``QDesktopWidget`` has been removed. ``QScreen`` should be used instead,
-  which can be retrieved using ``QWidget.screen()``,
-  ``QGuiApplication.primaryScreen()`` or ``QGuiApplication.screens()``.
-* ``QFontMetrics.width()`` has been renamed to ``horizontalAdvance()``.
-* ``QMouseEvent.pos()`` and ``QMouseEvent.globalPos()`` returning a ``QPoint``
-  as well as ``QMouseEvent.x()`` and ``QMouseEvent.y()`` returning ``int``
-  are now deprecated. ``QMouseEvent.position()`` and
-  ``QMouseEvent.globalPosition()`` returning a ``QPointF`` should be used
-  instead.
-* ``Qt.MidButton`` has been renamed to ``Qt.MiddleButton``.
-* ``QOpenGLVersionFunctionsFactory.get()`` instead of
-  ``QOpenGLContext.versionFunctions()`` should be used to obtain
-  *Open GL* functions.
-* ``QRegExp`` has been replaced by ``QRegularExpression``.
-* ``QWidget.mapToGlobal()`` and ``QWidget.mapFromGlobal()`` now also accept
-  and return ``QPointF``.
-* Functions named ``exec_`` (classes ``QCoreApplication``, ``QDialog``,
-  ``QEventLoop``) have been renamed to ``exec`` which became possible
-  in Python 3.
-
-More information can be found in the
-`Porting to Qt 6 <https://doc.qt.io/qt-6/portingguide.html>`_ Guide
-and the `Qt 6.2 Documentation <https://doc.qt.io/qt-6/index.html>`_ .
diff --git a/sources/pyside6/doc/gettingstarted/windows.rst b/sources/pyside6/doc/gettingstarted/windows.rst
deleted file mode 100644 (file)
index f688acd..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-Getting Started on Windows
-==========================
-
-The Qt library has to be built with the same version of MSVC as Python and PySide, this can be
-selected when using the online installer.
-
-Requirements
-------------
-
-* `MSVC2022`_ or (MSVC2019) for Python 3 on Windows,
-* `OpenSSL`_ (optional for SSL support, Qt must have been configured using the same SSL library).
-* ``sphinx`` package for the documentation (optional).
-* Check the platform dependencies of `Qt for Windows`_.
-
-.. note:: The Python provided by the Microsoft Store is not compatible with PySide. Please
-    use https://www.python.org/downloads/ to get a Python Interpreter.
-
-.. _MSVC2022: https://visualstudio.microsoft.com/downloads/
-.. _OpenSSL: https://sourceforge.net/projects/openssl/
-.. _`Qt for Windows`: https://doc.qt.io/qt-6/windows.html
-
-Building from source on Windows 10
-----------------------------------
-
-Creating a virtual environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
-which arbitrary modules can be installed and which can be removed after use::
-
-    python -m venv testenv
-    call testenv\Scripts\activate
-
-will create and use a new virtual environment, which is indicated by the command prompt changing.
-
-Setting up CLANG
-~~~~~~~~~~~~~~~~
-
-libclang can be downloaded from the
-`Qt servers <https://download.qt.io/development_releases/prebuilt/libclang>`_.
-for example, ``libclang-release_140-based-windows-vs2019_64.7z``.
-
-Note that from version 12 onwards, the prebuilt Windows binaries from
-`LLVM <https://www.llvm.org>`_ no longer contain CMake configuration files; so
-they can no longer be used.
-
-Extract the files, and leave it on any desired path, for example, ``c:``,
-and set the environment variable required::
-
-    set LLVM_INSTALL_DIR=c:\libclang
-    set PATH=C:\libclang\bin;%PATH%
-
-Getting PySide
-~~~~~~~~~~~~~~
-
-Cloning the official repository can be done by::
-
-    git clone https://code.qt.io/pyside/pyside-setup
-
-Checking out the version that we want to build, for example, 6.5::
-
-    cd pyside-setup && git checkout 6.5
-
-Install the general dependencies::
-
-    pip install -r requirements.txt
-
-.. note:: Keep in mind you need to use the same version as your Qt installation
-
-Building PySide
-~~~~~~~~~~~~~~~
-
-Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``C:\Qt\6.5.0\msvc2019_64\bin\qtpaths.exe``.
-
-Build can take a few minutes, so it is recommended to use more than one CPU core::
-
-    python setup.py build --qtpaths=c:\path\to\qtpaths.exe --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
-
-.. _creating_windows_debug_builds:
-
-Creating Debug Builds
-~~~~~~~~~~~~~~~~~~~~~
-
-* Choose *Custom Installation* when installing Python and tick the options for
-  debug binaries and libraries
-
-* Use ``venv`` to create a virtual environment and pass the debug binary::
-
-   python_d.exe -m venv testenv_d
-
-* Use ``python_d.exe`` to invoke ``setup.py``
-
-.. note:: Make sure you add the ``--debug`` option to the ``python setup.py install`` to produce a debug build
-
-
-Installing PySide
-~~~~~~~~~~~~~~~~~
-
-To install on the current directory, just run::
-
-    python setup.py install --qtpaths=c:\path\to\qtpaths.exe  --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
-
-Test installation
-~~~~~~~~~~~~~~~~~
-
-You can execute one of the examples to verify the process is properly working.
-Remember to properly set the environment variables for Qt and PySide::
-
-    python examples/widgets/widgets/tetrix.py
diff --git a/sources/pyside6/doc/images/screenshot_hello.png b/sources/pyside6/doc/images/screenshot_hello.png
deleted file mode 100644 (file)
index 41ebbf0..0000000
Binary files a/sources/pyside6/doc/images/screenshot_hello.png and /dev/null differ
diff --git a/sources/pyside6/doc/images/screenshot_hello_quick.png b/sources/pyside6/doc/images/screenshot_hello_quick.png
new file mode 100644 (file)
index 0000000..90e82d1
Binary files /dev/null and b/sources/pyside6/doc/images/screenshot_hello_quick.png differ
diff --git a/sources/pyside6/doc/images/screenshot_hello_widgets.png b/sources/pyside6/doc/images/screenshot_hello_widgets.png
new file mode 100644 (file)
index 0000000..41ebbf0
Binary files /dev/null and b/sources/pyside6/doc/images/screenshot_hello_widgets.png differ
index 1bb28f9c12b395209224756772e01f1615c82759..8c614978af148be4e8e6a5cec23c6b5e188caf81 100644 (file)
@@ -7,7 +7,7 @@
     which enables you to use Python to write your Qt applications.
     The project has two main components:
 
-    * `PySide6`_, so that you can use Qt6 APIs in your Python applications, and
+    * :ref:`PySide6 <getting-started>`, so that you can use Qt6 APIs in your Python applications, and
     * `Shiboken6 <shiboken6/index.html>`__, a binding generator tool, which can
       be used to expose C++ projects to Python, and a Python module with
       some utility functions.
     which enables you to use Python to write your Qt applications.
     The project has two main components:
 
-    * `PySide6`_, so that you can use Qt6 APIs in your Python applications, and
+    * :ref:`PySide6 <getting-started>`, so that you can use Qt6 APIs in your Python applications, and
     * `Shiboken6 <../shiboken6/index.html>`__, a binding generator tool, which can
       be used to expose C++ projects to Python, and a Python module with
       some utility functions.
 
-`Porting from PySide2 to PySide6`_ provides information on porting existing PySide2
-applications.
+:ref:`Porting from PySide2 to PySide6 <faq-porting-from2>` provides information on porting existing
+PySide2 applications.
 
 This project is available under the LGPLv3/GPLv3 and the `Qt commercial license`_.
 
 .. _Qt: https://doc.qt.io
-.. _PySide6: quickstart.html
-.. _`Qt commercial license`: https://www.qt.io/licensing/
-.. _`Porting from PySide2 to PySide6`: gettingstarted/porting_from2.html
+.. _`Qt commercial license`: https://www.qt.io/licensing/`
 
 Quick Start
 ===========
 
 You can obtain the latest stable version by running ``pip install pyside6``.
-If you want to build it yourself, check the `getting started guide`_.
+If you want to build it yourself, check the :ref:`Building from Source <buildingfromsource>` guide.
 
-To learn how to use it, check out `write your first application`_,
+To learn how to use it, check out :ref:`write your first application <gettingstarted>`,
 and to learn what is installed with the ``pyside6``, check the
-`package content, structure, and tools`_ page.
-
-
-.. _`getting started guide`: gettingstarted/index.html
-.. _`write your first application`: quickstart.html
-.. _`package content, structure, and tools`: gettingstarted/package_details.html
+:ref:`package content, structure, and tools <package_details>` page.
 
 Documentation
 =============
@@ -60,7 +53,7 @@ Documentation
 
         Write your first Qt application.
         +++
-        .. button-ref:: quick-start
+        .. button-ref:: getting-started
             :color: primary
             :outline:
             :expand:
@@ -71,9 +64,9 @@ Documentation
         :img-top: images/Support.svg
         :class-item: text-center
 
-        Modules, docs, and cross compilation.
+        Building from Source.
         +++
-        .. button-ref:: gettingstarted/index
+        .. button-ref:: building_from_source/index
             :color: primary
             :outline:
             :expand:
index 00e0ac4867414cd3f228aa830ae6463aa52b3aea..2796226e1a5e97780b876dac07d20f494ef4f9eb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import sys
 
@@ -31,7 +32,7 @@ def format_dict(d):
     return result
 
 
-class InheritanceGraph(object):
+class InheritanceGraph:
     """
     Given a list of classes, determines the set of classes that they inherit
     from all the way to the root "object", and then is able to generate a
index 06be33ea46bb76bb23e8a5a18ff35928b3eaa8fe..2211146809fa2eadbe57776ef4097292c76a5d00 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import json
 import os
index 8e77a46168964543c87ee3ddb283a09c374f767a..d3e4f7bb04bd6eb60cd9eed8e67fe3733181c3e9 100644 (file)
@@ -10,127 +10,143 @@ Qt Modules Supported by Qt for Python
 .. grid:: 1 3 3 3
     :gutter: 2
 
-    .. grid-item-card:: :mod:`QtBluetooth <PySide6.QtBluetooth>`
+    .. grid-item-card:: :mod:`Qt Bluetooth <PySide6.QtBluetooth>`
 
-        The Bluetooth API provides connectivity between Bluetooth enabled
-        devices.
+        Provides access to Bluetooth hardware.
 
+    .. grid-item-card:: :mod:`Qt Charts <PySide6.QtCharts>`
 
-    .. grid-item-card:: :mod:`QtCharts <PySide6.QtCharts>`
+        UI Components for displaying visually pleasing charts,
+        driven by static or dynamic data models.
 
-        Provides a set of easy to use chart components.
+    .. grid-item-card:: :mod:`Qt Concurrent <PySide6.QtConcurrent>`
 
-    .. grid-item-card:: :mod:`QtConcurrent <PySide6.QtConcurrent>`
+        Classes for writing multi-threaded programs without using low-level
+        threading primitives.
 
-        Provides high-level APIs that make it possible to write multi-threaded
-        programs without using low-level threading primitives such as mutexes,
-        read-write locks, wait conditions, or semaphores.
+    .. grid-item-card:: :mod:`Qt Core <PySide6.QtCore>`
 
+        Core non-graphical classes used by other modules.
 
-    .. grid-item-card:: :mod:`QtCore <PySide6.QtCore>`
+    .. grid-item-card:: :mod:`Qt Data Visualization <PySide6.QtDataVisualization>`
 
-        Provides core non-GUI functionality.
+        UI Components for creating stunning 3D data visualizations.
 
-    .. grid-item-card:: :mod:`QtDataVisualization <PySide6.QtDataVisualization>`
+    .. grid-item-card:: :mod:`Qt D-Bus <PySide6.QtDBus>`
 
-        Provides a way to visualize data in 3D as bar, scatter, and surface
-        graphs.
+        Classes for inter-process communication over the D-Bus protocol.
 
-    .. grid-item-card:: :mod:`QtDBus <PySide6.QtDBus>`
+    .. grid-item-card:: :mod:`Qt Designer <PySide6.QtDesigner>`
 
-        D-Bus is an Inter-Process Communication (IPC) and Remote Procedure
-        Calling (RPC) mechanism originally developed for Linux to replace
-        existing and competing IPC solutions with one unified protocol
+        Provides classes to extend *Qt Widgets Designer*.
 
-    .. grid-item-card:: :mod:`QtDesigner <PySide6.QtDesigner>`
+    .. grid-item-card:: :mod:`Qt GUI <PySide6.QtGui>`
 
-        Provides classes to extend *Qt Widgets Designer*.
+        Base classes for graphical user interface (GUI) components.
+
+    .. grid-item-card:: :mod:`Qt Graphs <PySide6.QtGraphs>`
+
+        Provides functionality for visualizing data in 3D as bar, scatter, and
+        surface graphs, as well as 2D in area, bar, donut, line, pie, scatter, and
+        spline graphs.
+
+    .. grid-item-card:: :mod:`QtGraphs Widgets <PySide6.QtGraphsWidgets>`
+
+        Provides the widget-based graphs API.
+
+    .. grid-item-card:: :mod:`Qt Help <PySide6.QtHelp>`
 
-    .. grid-item-card:: :mod:`QtGui <PySide6.QtGui>`
+        Classes for integrating documentation into applications.
 
-        Extends QtCore with GUI functionality.
+    .. grid-item-card:: :mod:`Qt HTTP Server <PySide6.QtHttpServer>`
 
-    .. grid-item-card:: :mod:`QtHelp <PySide6.QtHelp>`
+        A framework for embedding an HTTP server into a Qt application.
 
-        Provides classes for integrating online documentation in applications.
+    .. grid-item-card:: :mod:`Qt Location <PySide6.QtLocation>`
+
+        Provides QML and Python interfaces to create location-aware applications.
 
     .. grid-item-card:: :mod:`Qt Multimedia <PySide6.QtMultimedia>`
 
-        Provides API for multimedia-specific use cases.
+        A rich set of QML types and Python classes to handle multimedia content.
+        Also includes APIs to handle camera access.
 
     .. grid-item-card:: :mod:`Qt Multimedia Widgets <PySide6.QtMultimediaWidgets>`
 
         Provides the widget-based multimedia API.
 
-    .. grid-item-card:: :mod:`QtNetwork <PySide6.QtNetwork>`
+    .. grid-item-card:: :mod:`Qt Network <PySide6.QtNetwork>`
 
-        Offers classes that let you to write TCP/IP clients and servers.
+        Classes to make network programming easier and more portable.
 
     .. grid-item-card:: :mod:`Qt Network Authorization <PySide6.QtNetworkAuth>`
 
-        Provides a set of APIs that enable Qt applications to obtain limited
-        access to online accounts and HTTP services without exposing users'
-        passwords.
+        Provides support for OAuth-based authorization to online services.
 
-    .. grid-item-card:: :mod:`QtNfc <PySide6.QtNfc>`
+    .. grid-item-card:: :mod:`Qt NFC <PySide6.QtNfc>`
 
-        The NFC API provides connectivity between NFC enabled devices.
+        Provides access to Near-Field communication (NFC) hardware. On desktop
+        platforms NDEF access is only supported for Type 4 tags.
 
-    .. grid-item-card:: :mod:`QtOpenGL <PySide6.QtOpenGL>`
+    .. grid-item-card:: :mod:`Qt OpenGL <PySide6.QtOpenGL>`
 
-        Offers classes that make it easy to use OpenGL in Qt applications.
+        Classes that make it easy to use OpenGL in Qt applications.
 
-    .. grid-item-card:: :mod:`QtOpenGL Widgets <PySide6.QtOpenGLWidgets>`
+    .. grid-item-card:: :mod:`Qt OpenGL Widgets <PySide6.QtOpenGLWidgets>`
 
-        Provides the OpenGLWidget class enabling OpenGL rendering for
-        a certain part of the widget tree.
+        Provides a widget for rendering OpenGL graphics.
 
     .. grid-item-card:: :mod:`Qt Positioning <PySide6.QtPositioning>`
 
-        Provides access to position, satellite info and area monitoring
-        classes.
+        Provides access to position, satellite info and area monitoring classes.
 
     .. grid-item-card:: :mod:`Qt PDF <PySide6.QtPdf>`
 
-        Classes and functions for rendering PDF documents.
+        Classes and functions for rendering PDF documents on desktop platforms.
 
     .. grid-item-card:: :mod:`Qt PDF Widgets <PySide6.QtPdfWidgets>`
 
         A PDF viewer widget.
 
-    .. grid-item-card:: :mod:`QtPrintSupport <PySide6.QtPrintSupport>`
+    .. grid-item-card:: :mod:`Qt Print Support <PySide6.QtPrintSupport>`
+
+        Classes to make printing easier and more portable.
 
-        Provides extensive cross-platform support for printing.
+    .. grid-item-card:: :mod:`Qt Qml <PySide6.QtQml>`
 
-    .. grid-item-card:: :mod:`QtQml <PySide6.QtQml>`
+        Classes for QML and JavaScript languages.
 
-        Python API for Qt QML.
+    .. grid-item-card:: :mod:`Qt Quick <PySide6.QtQuick>`
 
-    .. grid-item-card:: :mod:`QtQuick <PySide6.QtQuick>`
+        A declarative framework for building highly dynamic applications
+        with custom UIs.
 
-        Provides classes for embedding Qt Quick in Qt applications.
+    .. grid-item-card:: :mod:`Qt Quick 3D <PySide6.QtQuick3D>`
 
-    .. grid-item-card:: :mod:`QtQuickControls2 <PySide6.QtQuickControls2>`
+        Provides a high-level API for creating 3D content or UIs based on Qt Quick.
 
-        Provides classes for setting up the controls from C++.
+    .. grid-item-card:: :mod:`Qt Quick Controls <PySide6.QtQuickControls2>`
 
-    .. grid-item-card:: :mod:`QtQuickTest <PySide6.QtQuickTest>`
+        Lightweight QML types for creating performant user interfaces for
+        desktop, embedded, and mobile devices.
 
-       A unit test framework for QML applications where test cases are written as JavaScript functions.
+    .. grid-item-card:: :mod:`Qt Quick Test <PySide6.QtQuickTest>`
 
-    .. grid-item-card:: :mod:`QtQuickWidgets <PySide6.QtQuickWidgets>`
+        A unit test framework for QML applications where test cases are written as JavaScript functions.
 
-        Provides the QQuickWidget class for embedding Qt Quick in widget-based applications.
+    .. grid-item-card:: :mod:`Qt Quick Widgets <PySide6.QtQuickWidgets>`
 
-    .. grid-item-card:: :mod:`QtRemoteObjects <PySide6.QtRemoteObjects>`
+        Provides a Python widget class for displaying a Qt Quick user interface.
 
-        Inter-Process Communication (IPC) module developed for Qt. This module
-        extends Qt's existing functionalities to enable information exchange
-        between processes or computers, easily.
+    .. grid-item-card:: :mod:`Qt Remote Objects <PySide6.QtRemoteObjects>`
 
-    .. grid-item-card:: :mod:`Qt Scxml <PySide6.QtScxml>`
+        Provides an easy to use mechanism for sharing a QObject's API
+        (Properties/Signals/Slots) between processes or devices.
 
-        Provides classes to create and use state machines from SCXML files.
+    .. grid-item-card:: :mod:`Qt SCXML <PySide6.QtScxml>`
+
+        Provides classes and tools for creating state machines from SCXML files and
+        embedding them in applications.
 
     .. grid-item-card:: :mod:`Qt Sensors <PySide6.QtSensors>`
 
@@ -147,46 +163,53 @@ Qt Modules Supported by Qt for Python
 
     .. grid-item-card:: :mod:`Qt Spatial Audio <PySide6.QtSpatialAudio>`
 
-        Provides APIs for modeling sound source and their surrounds in 3D
-        space.
+        Provides support for spatial audio. Create sound scenes in 3D space containing
+        different sound sources and room related properties such as reverb.
 
-    .. grid-item-card:: :mod:`QtSql <PySide6.QtSql>`
+    .. grid-item-card:: :mod:`Qt SQL <PySide6.QtSql>`
 
-        Helps you provide seamless database integration to your Qt
-        applications.
+        Classes for database integration using SQL.
 
-    .. grid-item-card:: :mod:`QtStateMachine <PySide6.QtStateMachine>`
+    .. grid-item-card:: :mod:`Qt State Machine <PySide6.QtStateMachine>`
 
         Provides classes for creating and executing state graphs.
 
-    .. grid-item-card:: :mod:`QtSvg <PySide6.QtSvg>`
+    .. grid-item-card:: :mod:`Qt SVG <PySide6.QtSvg>`
 
-        Provides classes for displaying the contents of SVG files.
+        Classes for displaying the contents of SVG files. Supports a subset of the
+        SVG 1.2 Tiny standard. A separate library (Qt SVG Widgets) provides support
+        for rendering SVG files in a widget UI.
 
-    .. grid-item-card:: :mod:`QtSvgWidgets <PySide6.QtSvgWidgets>`
+    .. grid-item-card:: :mod:`Qt SVG Widgets <PySide6.QtSvgWidgets>`
 
-        Provides a widget that is used to display the contents of SVG files.
+        Provides support for rendering SVG files in a widget UI.
 
-    .. grid-item-card:: :mod:`QtTest <PySide6.QtTest>`
+    .. grid-item-card:: :mod:`Qt Test <PySide6.QtTest>`
 
         Provides classes for unit testing Qt applications and libraries.
 
-    .. grid-item-card:: :mod:`QtUiTools <PySide6.QtUiTools>`
+    .. grid-item-card:: :mod:`QtTextToSpeech <PySide6.QtTextToSpeech>`
+
+        Provides support for synthesizing speech from text and playing it as audio
+        output.
 
-        Provides classes to handle forms created with *Qt Widgets Designer*.
+    .. grid-item-card:: :mod:`Qt UI Tools <PySide6.QtUiTools>`
+
+        Classes for loading QWidget based forms created in *Qt Widgets Designer*
+        dynamically, at runtime.
 
     .. grid-item-card:: :mod:`Qt WebChannel <PySide6.QtWebChannel>`
 
         Provides access to QObject or QML objects from HTML clients for
         seamless integration of Qt applications with HTML/JavaScript clients.
 
-    .. grid-item-card:: :mod:`QtWebEngine Core C++ Classes <PySide6.QtWebEngineCore>`
+    .. grid-item-card:: :mod:`QtWebEngine Core Classes <PySide6.QtWebEngineCore>`
 
         Provides public API shared by both QtWebEngine and QtWebEngineWidgets.
 
-    .. grid-item-card:: :mod:`QtWebEngine Widgets C++ Classes <PySide6.QtWebEngineWidgets>`
+    .. grid-item-card:: :mod:`QtWebEngine Widgets Classes <PySide6.QtWebEngineWidgets>`
 
-        Provides C++ classes for rendering web content in a QWidget based
+        Provides Python classes for rendering web content in a QWidget based
         application.
 
     .. grid-item-card:: :mod:`QtWebEngine QML Types <PySide6.QtWebEngineQuick>`
@@ -197,35 +220,43 @@ Qt Modules Supported by Qt for Python
 
         Provides WebSocket communication compliant with RFC 6455.
 
-    .. grid-item-card:: :mod:`QtWidgets <PySide6.QtWidgets>`
+    .. grid-item-card:: :mod:`Qt WebView <PySide6.QtWebView>`
+
+        Displays web content in a QML application by using APIs native to the platform,
+        without the need to include a full web browser stack.
+
+    .. grid-item-card:: :mod:`Qt Widgets <PySide6.QtWidgets>`
 
-        Extends Qt GUI with C++ widget functionality.
+        Classes to extend Qt GUI with Python widgets.
 
-    .. grid-item-card:: :mod:`QtXml <PySide6.QtXml>`
+    .. grid-item-card:: :mod:`Qt XML <PySide6.QtXml>`
 
-        Provides a C++ implementation of DOM.
+        Handling of XML in a Document Object Model (DOM) API.
 
-    .. grid-item-card:: :mod:`Qt3DAnimation <PySide6.Qt3DAnimation>`
+    .. grid-item-card:: :mod:`Qt 3D Animation Classes <PySide6.Qt3DAnimation>`
 
-        Provides basic elements required to animate 3D objects.
+        The Qt 3D Animation modules provides a set of prebuilt elements to
+        help you get started with Qt 3D.
 
-    .. grid-item-card:: :mod:`Qt3D Core <PySide6.Qt3DCore>`
+    .. grid-item-card:: :mod:`Qt 3D Core Classes <PySide6.Qt3DCore>`
 
-        Contains functionality to support near-realtime simulation systems.
+        The Qt 3D module contains functionality to support near-realtime
+        simulation systems.
 
-    .. grid-item-card:: :mod:`Qt3D Extras <PySide6.Qt3DExtras>`
+    .. grid-item-card:: :mod:`Qt 3D Extras Classes <PySide6.Qt3DExtras>`
 
         Provides a set of prebuilt elements to help you get started with Qt 3D.
 
-    .. grid-item-card:: :mod:`Qt3D Input <PySide6.Qt3DInput>`
+    .. grid-item-card:: :mod:`Qt 3D Input Classes<PySide6.Qt3DInput>`
 
-        Provides classes for handling user input in applications using Qt 3D.
+        Provides classes for handling user input in
+        applications using Qt3D.
 
-    .. grid-item-card:: :mod:`Qt3D Logic <PySide6.Qt3DLogic>`
+    .. grid-item-card:: :mod:`Qt 3D Logic Classes <PySide6.Qt3DLogic>`
 
         Enables synchronizing frames with the Qt 3D backend.
 
-    .. grid-item-card:: :mod:`Qt3D Render <PySide6.Qt3DRender>`
+    .. grid-item-card:: :mod:`Qt 3D Render Classes <PySide6.Qt3DRender>`
 
         Contains functionality to support 2D and 3D rendering using Qt 3D.
 
diff --git a/sources/pyside6/doc/package_details.rst b/sources/pyside6/doc/package_details.rst
new file mode 100644 (file)
index 0000000..113edb6
--- /dev/null
@@ -0,0 +1,75 @@
+.. _package_details:
+
+Package Details
+===============
+
+Having a large project as the Qt Framework available from one simple
+installation line::
+
+    pip install pyside6
+
+is really beneficial,
+but it might be confusing to newcomers.
+
+Besides your IDE, you don't need to install anything else to develop your
+Qt application, because the same command installs many tools
+that will help you design UIs, use QML types, generate
+files automatically, translate applications, etc.
+
+Package Dependencies
+--------------------
+
+.. image:: packages.png
+   :width: 400
+   :alt: Packages structure and dependency
+
+Starting from 6.3.0, the ``pyside6`` package (wheel) is almost empty,
+and only includes references to other packages that are required
+to properly use all the modules.
+This packages are:
+
+* ``pyside6-essentials``, `essential Qt modules <https://pypi.org/project/PySide6-Essentials/>`_,
+* ``pyside6-addons``, `additional Qt modules <https://pypi.org/project/PySide6-Addons/>`_,
+* ``shiboken6``, a utility Python module.
+
+You can verify this by running ``pip list`` to check the installed
+packages in your Python (virtual) environment::
+
+  (env) % pip list
+  Package            Version
+  ------------------ -------
+  pip                22.0.4
+  PySide6            6.3.0
+  PySide6-Addons     6.3.0
+  PySide6-Essentials 6.3.0
+  setuptools         58.1.0
+  shiboken6          6.3.0
+
+Both ``pyside6-essentials`` and ``pyside6-addons`` contain Qt binaries
+(``.so``, ``.dll``, or ``.dylib``) that are used by the Python wrappers
+that enable you to use the Qt modules from Python.
+For example, in the ``QtCore`` module, you will find
+on Linux:
+
+* ``PySide6/QtCore.abi3.so``, and
+* ``PySide6/Qt/lib/libQt6Core.so.6``
+
+inside the ``site-packages`` directory of your (virtual) environment.
+The first is the *importable* module which depends on the second file
+which is the original QtCore library.
+
+.. note:: The package ``shiboken6-generator`` is not a dependency,
+   and it's not available on PyPi. The reason, is that it depends on
+   ``libclang``, which is a large library that we don't package, and
+   requires a special configuration for you to use. Check the `Shiboken
+   Documentation`_ for more details.
+
+..
+  Adding the full URL because it's a different sphinx project.
+.. _`Shiboken Documentation`: https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html
+
+Tools Included
+--------------
+
+PySide6 comes bundled with a set of tools that assist in making the development experience with
+PySide6 more efficient. The list of tools can be found :ref:`here <package_tools>`.
diff --git a/sources/pyside6/doc/packages.png b/sources/pyside6/doc/packages.png
new file mode 100644 (file)
index 0000000..57e7ca4
Binary files /dev/null and b/sources/pyside6/doc/packages.png differ
index 7807c9d5265af1bef261407445c4ec349f7d94d2..2339feaad26b10f9ec6da4c0de12b35194624bef 100644 (file)
@@ -14,6 +14,22 @@ spurious                 += "Hostile character .*"
 #excludes qdoc statements that are not relevant for PySide6
 defines                  += qtforpython
 
+# Dummies for Apple platform versions
+macro.macOSMinSdkVersion = ""
+macro.macOSMinXcodeVersion = ""
+macro.macOSMinVersion = ""
+macro.macOSMaxVersionTested = ""
+
+macro.iOSMinSdkVersion = ""
+macro.iOSMinXcodeVersion = ""
+macro.iOSMinVersion = ""
+macro.iOSMaxVersionTested = ""
+
+macro.visionOSMinSdkVersion = ""
+macro.visionOSMinXcodeVersion = ""
+macro.visionOSMinVersion = ""
+macro.visionOSMaxVersionTested = ""
+
 includepaths += \
     -I @QT_INCLUDE_DIR@ \
     -I @mkspecInclude@ \
index d9d4343663413aaabc11bf633d5decf084468ce9..95342d4335d2dad32a3884333da98b405c54520a 100644 (file)
@@ -35,16 +35,16 @@ def run_qdoc(file, qdoc_args, args):
     duration = round(time.time() - start_time)
 
     if args.verbose or returncode != 0 or err:
-       cmd_str = " ".join(command)
-       print(f"> Output of: {cmd_str}")
-       if out:
-           print(out.decode("utf-8"), file=sys.stdout)
-       if err:
+        cmd_str = " ".join(command)
+        print(f"> Output of: {cmd_str}")
+        if out:
+            print(out.decode("utf-8"), file=sys.stdout)
+        if err:
             print(err.decode("utf-8"), file=sys.stderr)
-       print(f"> Finished: {file} {duration}s (exit code {returncode})")
+        print(f"> Finished: {file} {duration}s (exit code {returncode})")
 
     if returncode != 0:
-       raise Exception(f"Failing executing the command {command} ({returncode})")
+        raise Exception(f"Failing executing the command {command} ({returncode})")
 
 
 def get_qdocconf_files():
@@ -81,7 +81,7 @@ if __name__ == "__main__":
 
     args = parser.parse_args()
 
-    core_index = Path(args.doc_data_dir) / "webxml" / "qtcore-index.webxml"
+    core_index = Path(args.doc_data_dir) / "qtcore" / "webxml" / "qtcore-index.webxml"
     if core_index.is_file():
         print(f"qdoc_spawner: {core_index} already exists, bailing out")
         sys.exit(0)
@@ -95,7 +95,8 @@ if __name__ == "__main__":
     try:
         # mode: -prepare -no-link-errors
         with Pool(int(parallel)) as p:
-            p.starmap(run_qdoc, [(str(f), ["-prepare", "-no-link-errors"], args) for f in files_prepare])
+            files = [(str(f), ["-prepare", "-no-link-errors"], args) for f in files_prepare]
+            p.starmap(run_qdoc, files)
 
         # mode: -single-exec
         with Pool(int(parallel)) as p:
index 677371c45cce5d2e3403333447b7d8fcb42163f4..1a46cb0323f8e9ad782bfe10dc77e67e09eac878 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 Tool to run qtattributionsscanner and convert its output to rst
diff --git a/sources/pyside6/doc/qtmodules/pyside-qtgraphs.qdocconf.in b/sources/pyside6/doc/qtmodules/pyside-qtgraphs.qdocconf.in
new file mode 100644 (file)
index 0000000..7f83171
--- /dev/null
@@ -0,0 +1,2 @@
+include(@QT_SRC_DIR@/../qtgraphs/src/doc/qtgraphs.qdocconf)
+include(../pyside-config.qdocconf)
diff --git a/sources/pyside6/doc/qtmodules/pyside-qthttpserver.qdocconf.in b/sources/pyside6/doc/qtmodules/pyside-qthttpserver.qdocconf.in
new file mode 100644 (file)
index 0000000..09f3f04
--- /dev/null
@@ -0,0 +1,2 @@
+include(@QT_SRC_DIR@/../qthttpserver/doc/config/qthttpserver.qdocconf)
+include(../pyside-config.qdocconf)
diff --git a/sources/pyside6/doc/qtmodules/pyside-qtlocation.qdocconf.in b/sources/pyside6/doc/qtmodules/pyside-qtlocation.qdocconf.in
new file mode 100644 (file)
index 0000000..388289e
--- /dev/null
@@ -0,0 +1,2 @@
+include(@QT_SRC_DIR@/../qtlocation/src/location/doc/qtlocation.qdocconf)
+include(../pyside-config.qdocconf)
diff --git a/sources/pyside6/doc/qtmodules/pyside-qtquick3d.qdocconf.in b/sources/pyside6/doc/qtmodules/pyside-qtquick3d.qdocconf.in
new file mode 100644 (file)
index 0000000..64f0cce
--- /dev/null
@@ -0,0 +1,2 @@
+include(@QT_SRC_DIR@/../qtquick3d/src/quick3d/doc/qtquick3d.qdocconf)
+include(../pyside-config.qdocconf)
diff --git a/sources/pyside6/doc/qtmodules/pyside-qtstatemachine.qdocconf.in b/sources/pyside6/doc/qtmodules/pyside-qtstatemachine.qdocconf.in
new file mode 100644 (file)
index 0000000..b724be1
--- /dev/null
@@ -0,0 +1,3 @@
+include(@QT_SRC_DIR@/../qtscxml/src/statemachine/doc/qtstatemachine.qdocconf)
+includepaths += -I @QT_SRC_DIR@/../qtscxml/src/scxml/doc
+include(../pyside-config.qdocconf)
diff --git a/sources/pyside6/doc/qtmodules/pyside-qtwebview.qdocconf.in b/sources/pyside6/doc/qtmodules/pyside-qtwebview.qdocconf.in
new file mode 100644 (file)
index 0000000..e9ae61c
--- /dev/null
@@ -0,0 +1,2 @@
+include(@QT_SRC_DIR@/../qtwebview/src/webview/doc/qtwebview.qdocconf)
+include(../pyside-config.qdocconf)
diff --git a/sources/pyside6/doc/quickstart.rst b/sources/pyside6/doc/quickstart.rst
deleted file mode 100644 (file)
index 7e1a210..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-.. _quick-start:
-
-Quick start
-===========
-
-New to Qt? Check also the :ref:`faq-section` section at the end of this page.
-In case you own a Qt License, please refer to :ref:`commercial-page`.
-
-.. note:: Having Qt installed in your system will not interfere with your
-   PySide6 installation if you do it via ``pip install``, because the Python
-   packages (wheels) include already Qt binaries. Most notably, style plugins
-   from the system won't have any effect on PySide applications.
-
-Requirements
-------------
-
-Before you can install |project|, first you must install the following software:
-
-* Python 3.7+,
-* We **highly** recommend using a virtual environment, such as
-  `venv <https://docs.python.org/3/library/venv.html>`_ or
-  `virtualenv <https://virtualenv.pypa.io/en/latest>`_
-  and avoid installing PySide6 via ``pip`` in your system.
-
-.. note:: For Windows users, please use the interpreter from https://python.org/download
-    rather than the one installed from the Microsoft Store.
-
-Installation
-------------
-
-.. note:: For a commercial installation, refer to :ref:`commercial-page`.
-
-* **Creating and activating an environment**
-  You can do this by running the following on a terminal:
-
-  * Create environment (Your Python executable might be called ``python3``)::
-
-        python -m venv env
-
-  * Activate the environment (Linux and macOS)::
-
-        source env/bin/activate
-
-  * Activate the environment (Windows)::
-
-        env\Scripts\activate.bat
-
-  Check this animation on how to do it:
-
-  .. image:: https://qt-wiki-uploads.s3.amazonaws.com/images/8/8a/Pyside6_install.gif
-     :alt: Installation GIF
-
-* **Installing PySide6**
-
-  Now you are ready to install the |project| packages using ``pip``.
-  From the terminal, run the following command:
-
-  * For the latest version::
-
-        pip install pyside6
-
-  * For a specific version, like 6.4.1::
-
-        pip install pyside6==6.4.1
-
-  * It is also possible to install a specific snapshot from our servers.
-    To do so, you can use the following command::
-
-      pip install --index-url=https://download.qt.io/snapshots/ci/pyside/6.4/latest pyside6 --trusted-host download.qt.io
-
-  .. note:: Starting with 6.4.3, PySide6 can be used from inside a
-    `conda <https://conda.io>`_ environment, but any manual changes you make to
-    the ``qt.conf`` file will be ignored. If you want to set custom values to
-    the Qt configuration, set them in a ``qt6.conf`` file instead.
-    Read more about `qt.conf`_.
-
-.. _`qt.conf`: https://doc.qt.io/qt-6/qt-conf.html
-
-* **Test your installation**
-
-  Now that you have |project| installed, test your setup by running the following Python
-  constructs to print version information::
-
-    import PySide6.QtCore
-
-    # Prints PySide6 version
-    print(PySide6.__version__)
-
-    # Prints the Qt version used to compile PySide6
-    print(PySide6.QtCore.__version__)
-
-.. note:: For more information about what's included in the ``pyside6``
-   package, check :ref:`package_details`.
-
-Create a Simple Qt Widgets Application
---------------------------------------
-
-Your |project| setup is ready. You can explore it further by developing a simple application
-that prints "Hello World" in several languages. The following instructions will
-guide you through the development process:
-
-* **Imports**
-
-  Create a new file named :code:`hello_world.py`, and add the following imports to it.::
-
-    import sys
-    import random
-    from PySide6 import QtCore, QtWidgets, QtGui
-
-  The |pymodname| Python module provides access to the Qt APIs as its submodule.
-  In this case, you are importing the :ref:`QtCore`, :ref:`QtWidgets`, and :ref:`QtGui` submodules.
-
-* **Main Class**
-
-  Define a class named :code:`MyWidget`, which extends :ref:`QWidget` and
-  includes a :ref:`QPushButton` and :ref:`QLabel`.::
-
-    class MyWidget(QtWidgets.QWidget):
-        def __init__(self):
-            super().__init__()
-
-            self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет Ð¼Ð¸Ñ€"]
-
-            self.button = QtWidgets.QPushButton("Click me!")
-            self.text = QtWidgets.QLabel("Hello World",
-                                         alignment=QtCore.Qt.AlignCenter)
-
-            self.layout = QtWidgets.QVBoxLayout(self)
-            self.layout.addWidget(self.text)
-            self.layout.addWidget(self.button)
-
-            self.button.clicked.connect(self.magic)
-
-        @QtCore.Slot()
-        def magic(self):
-            self.text.setText(random.choice(self.hello))
-
-  The ``MyWidget`` class has the :code:`magic` member function that randomly
-  chooses an item from the :code:`hello` list. When you click the button, the
-  :code:`magic` function is called.
-
-* **Application execution**
-
-  Now, add a main function where you instantiate :code:`MyWidget` and :code:`show` it.::
-
-    if __name__ == "__main__":
-        app = QtWidgets.QApplication([])
-
-        widget = MyWidget()
-        widget.resize(800, 600)
-        widget.show()
-
-        sys.exit(app.exec())
-
-  Run your example by writing the following command: :command:`python hello_world.py`.
-
-  Try clicking the button at the bottom to see which greeting you get.
-
-  .. image:: images/screenshot_hello.png
-     :alt: Hello World application
-
-Create a Simple Quick Application
----------------------------------
-
-To do the same using Qt Quick:
-
-* **Imports**
-
-  Create a new file named :code:`hello_world_quick.py`, and add the following imports to it.::
-
-    import sys
-    from PySide6.QtGui import QGuiApplication
-    from PySide6.QtQml import QQmlApplicationEngine
-
-* **Declarative UI**
-
-  The UI can be described in the QML language (assigned to a Python variable)::
-
-    QML = """
-    import QtQuick
-    import QtQuick.Controls
-    import QtQuick.Layouts
-
-    Window {
-        width: 300
-        height: 200
-        visible: true
-        title: "Hello World"
-
-        readonly property list<string> texts: ["Hallo Welt", "Hei maailma",
-                                               "Hola Mundo", "Привет Ð¼Ð¸Ñ€"]
-
-        function setText() {
-            var i = Math.round(Math.random() * 3)
-            text.text = texts[i]
-        }
-
-        ColumnLayout {
-            anchors.fill:  parent
-
-            Text {
-                id: text
-                text: "Hello World"
-                Layout.alignment: Qt.AlignHCenter
-            }
-            Button {
-                text: "Click me"
-                Layout.alignment: Qt.AlignHCenter
-                onClicked:  setText()
-            }
-        }
-    }
-    """
-
-  .. note:: Keep in mind ideally this content should go into
-    a ``qml`` file, but for simplicity, we are using a string variable.
-
-* **Application execution**
-
-  Now, add a main function where you instantiate a :ref:`QQmlApplicationEngine` and
-  load the QML::
-
-    if __name__ == "__main__":
-        app = QGuiApplication(sys.argv)
-        engine = QQmlApplicationEngine()
-        engine.loadData(QML.encode('utf-8'))
-        if not engine.rootObjects():
-            sys.exit(-1)
-        exit_code = app.exec()
-        del engine
-        sys.exit(exit_code)
-
-
-  .. note:: This is a simplified example. Normally, the QML code should be in a separate
-     :code:`.qml` file, which can be edited by design tools.
-
-.. _faq-section:
-
-Frequently Asked Questions
---------------------------
-
-Here you can find a couple of common questions and situations that will
-clarify questions before you start programming.
-
-.. grid:: 1 3 3 3
-    :gutter: 2
-
-    .. grid-item-card:: What is Qt
-        :link: faq/whatisqt.html
-
-        Qt, QML, Widgets... What is the difference?
-
-    .. grid-item-card:: Compatible IDEs
-        :link: faq/whichide.html
-
-        Which IDEs are compatible with PySide?
-
-    .. grid-item-card:: Binding Generation
-        :link: faq/whatisshiboken.html
-
-        What is Shiboken?
-
-    .. grid-item-card:: File types
-        :link: faq/typesoffiles.html
-
-        File Types in PySide
-
-    .. grid-item-card:: App distribution
-        :link: faq/distribution.html
-
-        Distributing your application to other systems and platforms
-
-    .. grid-item-card:: Why Qt for Python?
-        :link: faq/whyqtforpython.html
-
-        As a Qt/C++ developer, why should I consider Qt for Python?
-
-.. toctree::
-    :hidden:
-
-    faq/whatisqt.rst
-    faq/whichide.rst
-    faq/whatisshiboken.rst
-    faq/typesoffiles.rst
-    faq/distribution.rst
-    faq/whyqtforpython.rst
-
index b421a428f37023e91b9b779254056b635ea45cc6..0f6cd03f09b719688ac6a90075057f08a18de294 100644 (file)
@@ -42,7 +42,7 @@ Widget Development
         :link-type: ref
 
         drag-and-drop tool for designing Widget UIs (generates ``.ui`` files,
-        see :ref:`using_ui_files`).
+        see :ref:`tutorial_uifiles`).
 
     .. grid-item-card:: ``pyside6-uic``
         :link: pyside6-uic
@@ -99,6 +99,12 @@ QML Development
         the capabilities of Qt's ``QML`` runtime utility by
         directly invoking QQmlEngine/QQuickView.
 
+    .. grid-item-card:: ``pyside6-svgtoqml``
+        :link: pyside6-svgtoqml
+        :link-type: ref
+
+        to convert SVG files to QML files.
+
 Translations
 ~~~~~~~~~~~~
 
@@ -209,3 +215,25 @@ Qt Quick 3D
         :link-type: ref
 
         a graphical user interface for the ``pyside6-balsam`` tool.
+
+.. toctree::
+    :hidden:
+
+    pyside-assistant
+    pyside-designer
+    pyside-genpyi
+    pyside-linguist
+    pyside-lrelease
+    pyside-lupdate
+    pyside-metaobjectdump
+    pyside-project
+    pyside-qml
+    pyside-qmlcachegen
+    pyside-qmllint
+    pyside-qmltyperegistrar
+    pyside-rcc
+    pyside-uic
+    pyside6-balsam
+    pyside6-balsamui
+    pyside6-qmlimportscanner
+    pyside6-qsb
index 4cde6b2bf72833ddf18d3c4857164ae35cc1aa0c..9cd28066faa21b11f007bb3fc7887cb38cc51045 100644 (file)
@@ -43,7 +43,7 @@ where you could adjust dimensions, names, etc.
 When your application is finished, you will save your design in a ``.ui``
 file. This ``.ui`` file can later be converted into a Python file,
 with the help of the :ref:`pyside6-uic` tool. You can find
-more details of this process in: :ref:`using_ui_files`.
+more details of this process in: :ref:`tutorial_uifiles`.
 
 If you are interested in more functionality of the tool, you can check
 the official `Qt Widgets Designer Manual`_.
@@ -85,7 +85,7 @@ We provide a helper class, ``QPyDesignerCustomWidgetCollection``, to
 simplify the registration process.
 
 If you are interested in all the details of this specific case, you can
-check the :ref:`task-menu-extension-example`.
+check the :ref:`example_designer_taskmenuextension`.
 
 .. _`Qt Widgets Designer`: https://doc.qt.io/qt-6/qtdesigner-manual.html
 .. _`Qt Widgets Designer Manual`: https://doc.qt.io/qt-6/qtdesigner-manual.html
index e13124a8e119e0d8de8628d3e7fb63efded7ee1a..af5e8e3375f28f5d3b28c2c274944b56df19d169 100644 (file)
@@ -11,7 +11,7 @@ version bundled with PySide and the one from Qt.
 
 For more information on how to use this tool, read Qt's documentation
 here: `Qt Linguist`_. Read more about how to translate PySide
-applications here: :ref:`translations`.
+applications here: :ref:`tutorial_translations`.
 
 .. image:: pyside6-linguist_screenshot.webp
     :width: 500
index 7c628f2aa37cc74648c716f9b825ab2aca5a11b8..458867fccb68e6cbd46e32f74e1722356b853028 100644 (file)
@@ -10,12 +10,12 @@ pyside6-lrelease
 ``pyside6-lrelease`` is a command line tool wrapping `lrelease`_. It produces
 ``.qm`` files out of ``.ts`` files. The ``.qm`` file format is a compact binary
 format that the localized application uses. It provides extremely fast lookup
-for translations (see :ref:`translations`).
+for translations (see :ref:`tutorial_translations`).
 
 Usage
 -----
 
-To convert a ``.ts`` file of the :ref:`qt-linguist-example`
+To convert a ``.ts`` file of the :ref:`example_widgets_linguist`
 into its binary representation, run:
 
 .. code-block:: bash
index 1c0d73c159266eb2e459b454d1433580089df24f..04a38b57d65a5812a306343b1ecb82e00b7e831d 100644 (file)
@@ -8,12 +8,12 @@ pyside6-lupdate
 
 ``pyside6-lupdate`` is a command line tool wrapping `lupdate`_. It finds
 translatable strings in Python, ``.ui``, and ``.qml`` files and generates or
-updates ``.ts`` files (see :ref:`translations`).
+updates ``.ts`` files (see :ref:`tutorial_translations`).
 
 Usage
 -----
 
-To create or update the ``.ts`` file of the :ref:`qt-linguist-example`,
+To create or update the ``.ts`` file of the :ref:`example_widgets_linguist`,
 run:
 
 .. code-block:: bash
index 0359e1b7d7ffb578d5e69f16d017a535797d9d02..e7cf813e49d883a14ae7ed7b943e527a4a2020c7 100644 (file)
@@ -42,7 +42,7 @@ It is also possible to specify a directory containing the project file.
 
 *build*
     Builds the project, generating the required build artifacts
-    (see :ref:`using_ui_files`, :ref:`using_qrc_files`).
+    (see :ref:`tutorial_uifiles`, :ref:`tutorial_qrcfiles`).
 
 *run*
     Builds the project and runs the main.
@@ -51,7 +51,7 @@ It is also possible to specify a directory containing the project file.
     Deploys the application (see see :ref:`pyside6-deploy`).
 
 *lupdate*
-    Updates translation (.ts) files (see :ref:`translations`).
+    Updates translation (.ts) files (see :ref:`tutorial_translations`).
 
 *clean*
     Cleans the build artifacts.
index 0502dd94a721bfcbbf482f82687672cc1a0cc745..6279aad2b42fbad85014954da7eaca5d4b84fb76 100644 (file)
@@ -3,16 +3,16 @@
 pyside6-qml
 ===========
 
-``pyside6-qml``  mimics some capabilities of Qt's `qml <qml_runtime>`_ runtime utility by directly
+``pyside6-qml``  mimics some capabilities of Qt's `qml`_ runtime utility by directly
 invoking QQmlEngine/QQuickView. It enables prototyping with QML/QtQuick without the need to write
-any Python code that loads the QML files either through `QQmlApplicationEngine <qqmlappengine>`_ or
-the `QQuickView <qquickview>`_ class. The tool also detects the QML classes implemented in Python
+any Python code that loads the QML files either through `QQmlApplicationEngine`_ or
+the `QQuickView`_ class. The tool also detects the QML classes implemented in Python
 and registers them with the QML type system.
 
 Usage
 -----
 
-Consider the example `Extending QML - Plugins Example <extending_qml_example>`_. This example does
+Consider the example `Extending QML - Plugins Example`_. This example does
 not have a Python file with a ``main`` function that initializes a QmlEngine to load the QML file
 ``app.qml``. You can run the example by running
 
@@ -50,8 +50,8 @@ Options
 * **--verbose/-v**: Run ``pyside6-qml`` in verbose mode. When run in this mode, pyside6-qml prints
   log messages during various stages of processing.
 
-Options that align with `QML <qml_runtime>`_ runtime utility
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Options that align with the `qml`_ runtime utility
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 * **--app-typ/-a**: Specifies which application class to use. It takes one of the three values -
   ``core, gui, widget``. The default value is *gui*.
@@ -78,7 +78,7 @@ Options that align with `QML <qml_runtime>`_ runtime utility
 
 * **--disable-context-sharing**: Disable the use of a shared GL context for QtQuick Windows".
 
-.. _`qml_runtime`: https://doc.qt.io/qt-6/qtquick-qml-runtime.html
-.. _`qqmlappengine`: https://doc.qt.io/qt-6/qqmlapplicationengine.html
-.. _`qquickview`: https://doc.qt.io/qt-6/qquickview.html
-.. _`extending_qml_example`: https://doc.qt.io/qtforpython-6/examples/example_qml_tutorials_extending-qml_chapter6-plugins.html
+.. _`qml`: https://doc.qt.io/qt-6/qtquick-qml-runtime.html
+.. _`QQmlApplicationEngine`: https://doc.qt.io/qt-6/qqmlapplicationengine.html
+.. _`QQuickView`: https://doc.qt.io/qt-6/qquickview.html
+.. _`Extending QML - Plugins Example`: https://doc.qt.io/qtforpython-6/examples/example_qml_tutorials_extending-qml_chapter6-plugins.html
index e5688485ebec4313d4f794d959c1df0f8bc3abfb..9bf11ba250c05758b53635285800d708e38c9808 100644 (file)
@@ -49,7 +49,6 @@ for example:
 For additional options, you can use ``pyside6-rcc -h`` in order to get
 more information about additional options.
 
-Visit the tutorial :ref:`using_qrc_files` for a hands-on example.
+Visit the tutorial :ref:`tutorial_qrcfiles` for a hands-on example.
 
 .. _`rcc`: https://doc.qt.io/qt-6/rcc.html
-
index ba4e36b4e007f0f896460d953162fe7f6e90404b..6588ee284716e913da8c683812906804e1b3d907 100644 (file)
@@ -35,7 +35,7 @@ and you will get one class called ``Ui_TheNameOfYourDesign(object)`` that
 is in charge of positioning all the elements like your design.
 
 To use this Python file, you should follow our tutorial in
-:ref:`using_ui_files`, but in summary, it is mainly importing the class
+:ref:`tutorial_uifiles`, but in summary, it is mainly importing the class
 from the generated file and setting it up in your code:
 
 .. code-block:: Python
@@ -50,10 +50,9 @@ translations, etc.
 .. note:: Remember that you need to have a class corresponding to the base
     form you selected in :ref:`pyside6-designer`, a ``QWidget``, or ``QDialog``,
     or ``QMainWindow``, etc, in order for ``setupUi`` to work. Check
-    :ref:`using_ui_files` for more information.
+    :ref:`tutorial_uifiles` for more information.
 
 .. warning:: Do not modify the content of the generated Python file from your
     ``.ui`` file, otherwise everything will be lost when you re-generate it.
 
 .. _`uic`: https://doc.qt.io/qt-6/uic.html
-
index c6677f6a3ee73bf1c2ee973ed908a262d81a9440..3c5180adad043785ce739bbfe1013e6e03b7b0a0 100644 (file)
@@ -3,7 +3,7 @@
 pyside6-balsam
 ==============
 
-``pyside6-qsb`` is a tool that wraps the `balsam <Balsam Asset Import Tool>`_
+``pyside6-qsb`` is a tool that wraps the `balsam <Balsam Asset Import Tool_>`_
 tool provided with Qt Quick 3D. The Balsam tool is a command line application
 that is part of Qt Quick 3D's asset conditioning pipeline. The purpose is to
 take assets created in digital content creation tools like `Maya`_, `3ds Max`_
@@ -56,4 +56,3 @@ For other modes of operation, refer to the `Balsam Asset Import Tool`_.
 .. _Maya: https://www.autodesk.com/products/maya/overview
 .. _3ds Max: https://www.autodesk.com/products/3ds-max/overview
 .. _Blender: https://www.blender.org/
-
index f34cb6045a6f3953a3d6460c842625141bb41717..d5b75d0145e50fac3089283daae5ebe02328164a 100644 (file)
@@ -19,4 +19,3 @@ documentation here: `Balsam Asset Import Tool`_.
 .. _Maya: https://www.autodesk.com/products/maya/overview
 .. _3ds Max: https://www.autodesk.com/products/3ds-max/overview
 .. _Blender: https://www.blender.org/
-
index ba27414d7b990bfbab9fd4538a47d55b2dc32112..69b162d8a7fdf2cc38088a342624a30f3091d0de 100644 (file)
@@ -14,7 +14,7 @@ the QML source files.
 Usage
 -----
 
-Invoking the tool in the directory of the :ref:`filesystemexplorer_example`
+Invoking the tool in the directory of the :ref:`example_quickcontrols_filesystemexplorer`
 example using:
 
 .. code-block:: bash
index f6f1847d4d5ed14d7111fcab53a4363ec0932ad0..c9d0c40f42a0a59954c9a51cc6cfd72cc0bbbed8 100644 (file)
@@ -3,7 +3,7 @@
 pyside6-qsb
 ===========
 
-``pyside6-qsb`` is a tool that wraps the `qsb <QSB Manual>`_ tool. qsb is a
+``pyside6-qsb`` is a tool that wraps the `qsb <QSB Manual_>`_ tool. qsb is a
 command line tool provided by the `Qt Shader Tools`_ module. It integrates
 third-party libraries such as `glslang`_ and `SPIRV-Cross`_, optionally invokes
 external tools, such as ``fxc`` or ``spirv-opt``, and generates .qsb files.
diff --git a/sources/pyside6/doc/tools/pyside6-svgtoqml.rst b/sources/pyside6/doc/tools/pyside6-svgtoqml.rst
new file mode 100644 (file)
index 0000000..3abf221
--- /dev/null
@@ -0,0 +1,16 @@
+.. _pyside6-svgtoqml:
+
+pyside6-svgtoqml
+================
+
+``pyside6-svgtoqml`` is a command line tool that converts SVG files to
+QML files. This QML file can then be used as a component in Qt Quick
+applications.
+
+.. note::
+
+    This tool is a wrapper around the Qt `svgtoqml <SVGtoQML Manual_>`_
+    tool. For more information on how to use this tool, refer to the
+    `svgtoqml <SVGtoQML Manual_>`_ manual.
+
+.. _`SVGtoQML Manual`: https://doc.qt.io/qt-6/qtqml-tooling-svgtoqml.html
index c5464640b6378f5f82cbd857ba8da3a3f1ce845f..dfda3f1ff63cb1270e4dab9a732595308aa39f9f 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_clickablebutton:
+
+
 Using a Simple Button
 =====================
 
index b7712672bfa58bfeb84e53c4e0df7e33180ee3c8..fd3e22bf92bac97e240b29b34d882565576966ec 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_dialog:
+
+
 Creating a Dialog Application
 =============================
 
index 49cd3d94ac93055cc3d4bbb7535140287114bb24..206383e16f9f43cb9fc1516f73ed6759a97a4cce 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_qml:
+
+
 Your First QtQuick/QML Application
 **********************************
 
@@ -8,12 +11,11 @@ user interface is specified as a tree of objects with properties. In
 this tutorial, we will show how to make a simple "Hello World"
 application with PySide6 and QML.
 
-A PySide6/QML application consists, at least, of two different files -
+A PySide6/QML application consists, mainly, of two different files -
 a file with the QML description of the user interface, and a python file
-that loads the QML file. To make things easier, let's save both files in
-the same directory.
+that loads the QML file.
 
-Here is a simple QML file called :code:`view.qml`:
+Here is a simple QML file called :code:`Main.qml`:
 
 .. code-block:: javascript
 
@@ -40,27 +42,38 @@ that reads "Hello World". The code :code:`anchors.centerIn: main` makes
 the text appear centered within the object with :code:`id: main`,
 which is the Rectangle in this case.
 
+Put the file into into a directory named :code:`App` along
+with a file named :code:`qmldir` to describe a basic QML module:
+
+.. code-block:: text
+
+    module App
+    Main 254.0 Main.qml
+
 Now, let's see how the code looks on the PySide6.
 Let's call it :code:`main.py`:
 
 .. code-block:: python
 
     import sys
-    from PySide6.QtWidgets import QApplication
+    from PySide6.QtGui import QGuiApplication
     from PySide6.QtQuick import QQuickView
 
     if __name__ == "__main__":
-        app = QApplication()
+        app = QGuiApplication()
         view = QQuickView()
-
-        view.setSource("view.qml")
+        view.engine().addImportPath(sys.path[0])
+        view.loadFromModule("App", "Main")
         view.show()
-        sys.exit(app.exec())
+        ex = app.exec()
+        del view
+        sys.exit(ex)
 
 If you are already familiar with PySide6 and have followed our
 tutorials, you have already seen much of this code.
-The only novelties are that you must :code:`import QtQuick` and set the
-source of the :code:`QQuickView` object to the URL of your QML file.
+The only novelties are that you must :code:`import QtQuick`,
+add the directory to the import paths, and instruct the
+:code:`QQuickView` to load our module.
 Then, similar to what you do with any Qt widget, you call
 :code:`QQuickView.show()`.
 
index 858293beb6e9ab246e77817bdaf2ded6ad62442d..b6861f9207c42059468e58621c019037025c832e 100644 (file)
@@ -1,4 +1,5 @@
-.. _using_qrc_files:
+.. _tutorial_qrcfiles:
+
 
 Using ``.qrc`` Files (``pyside6-rcc``)
 **************************************
@@ -78,87 +79,88 @@ To use the generated file, add the following import at the top of your main Pyth
 Changes in the code
 ===================
 
-As you are modifying an existing example, you need to modify the following
-lines:
+As you are modifying an existing example, you need to modify the
+``player.py`` file. At the top, change
 
 .. code-block:: python
 
     from PySide6.QtGui import QIcon, QKeySequence
-    playIcon = self.style().standardIcon(QStyle.SP_MediaPlay)
-    previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward)
-    pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause)
-    nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward)
-    stopIcon = self.style().standardIcon(QStyle.SP_MediaStop)
 
-and replace them with the following:
+to:
 
 .. code-block:: python
 
     from PySide6.QtGui import QIcon, QKeySequence, QPixmap
-    playIcon = QIcon(QPixmap(":/icons/play.png"))
-    previousIcon = QIcon(QPixmap(":/icons/previous.png"))
-    pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
-    nextIcon = QIcon(QPixmap(":/icons/forward.png"))
-    stopIcon = QIcon(QPixmap(":/icons/stop.png"))
 
-This ensures that the new icons are used instead of the default ones provided
-by the application theme.
-Notice that the lines are not consecutive, but are in different parts
-of the file.
-
-After all your imports, add the following
+Below the imports, add the following:
 
 .. code-block:: python
 
     import rc_icons
 
-Now, the constructor of your class should look like this:
+In the ``MainWindow.__init__()`` function, replace the code
+loading the icons from the theme by your custom icons:
 
 .. code-block:: python
 
-    def __init__(self):
-        super(MainWindow, self).__init__()
-
-        self.playlist = QMediaPlaylist()
-        self.player = QMediaPlayer()
+    playIcon = QIcon(QPixmap(":/icons/play.png"))
+    self._play_action = tool_bar.addAction(playIcon, "Play")
 
-        toolBar = QToolBar()
-        self.addToolBar(toolBar)
+When doing this for all icons, the constructor of your class should
+look like this:
 
-        fileMenu = self.menuBar().addMenu("&File")
-        openAction = QAction(QIcon.fromTheme("document-open"),
-                             "&Open...", self, shortcut=QKeySequence.Open,
-                             triggered=self.open)
-        fileMenu.addAction(openAction)
-        exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit",
-                             self, shortcut="Ctrl+Q", triggered=self.close)
-        fileMenu.addAction(exitAction)
+.. code-block:: python
 
-        playMenu = self.menuBar().addMenu("&Play")
+    def __init__(self):
+        super().__init__()
+
+        self._playlist = []
+        self._playlist_index = -1
+        self._audio_output = QAudioOutput()
+        self._player = QMediaPlayer()
+        self._player.setAudioOutput(self._audio_output)
+
+        self._player.errorOccurred.connect(self._player_error)
+
+        tool_bar = QToolBar()
+        self.addToolBar(tool_bar)
+
+        file_menu = self.menuBar().addMenu("&File")
+        icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen)
+        open_action = QAction(icon, "&Open...", self,
+                              shortcut=QKeySequence.Open, triggered=self.open)
+        file_menu.addAction(open_action)
+        tool_bar.addAction(open_action)
+        icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit)
+        exit_action = QAction(icon, "E&xit", self,
+                              shortcut="Ctrl+Q", triggered=self.close)
+        file_menu.addAction(exit_action)
+
+        play_menu = self.menuBar().addMenu("&Play")
         playIcon = QIcon(QPixmap(":/icons/play.png"))
-        self.playAction = toolBar.addAction(playIcon, "Play")
-        self.playAction.triggered.connect(self.player.play)
-        playMenu.addAction(self.playAction)
+        self._play_action = tool_bar.addAction(playIcon, "Play")
+        self._play_action.triggered.connect(self._player.play)
+        play_menu.addAction(self._play_action)
 
         previousIcon = QIcon(QPixmap(":/icons/previous.png"))
-        self.previousAction = toolBar.addAction(previousIcon, "Previous")
-        self.previousAction.triggered.connect(self.previousClicked)
-        playMenu.addAction(self.previousAction)
+        self._previous_action = tool_bar.addAction(previousIcon, "Previous")
+        self._previous_action.triggered.connect(self.previous_clicked)
+        play_menu.addAction(self._previous_action)
 
         pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
-        self.pauseAction = toolBar.addAction(pauseIcon, "Pause")
-        self.pauseAction.triggered.connect(self.player.pause)
-        playMenu.addAction(self.pauseAction)
+        self._pause_action = tool_bar.addAction(pauseIcon, "Pause")
+        self._pause_action.triggered.connect(self._player.pause)
+        play_menu.addAction(self._pause_action)
 
         nextIcon = QIcon(QPixmap(":/icons/forward.png"))
-        self.nextAction = toolBar.addAction(nextIcon, "Next")
-        self.nextAction.triggered.connect(self.playlist.next)
-        playMenu.addAction(self.nextAction)
+        self._next_action = tool_bar.addAction(nextIcon, "Next")
+        self._next_action.triggered.connect(self.next_clicked)
+        play_menu.addAction(self._next_action)
 
         stopIcon = QIcon(QPixmap(":/icons/stop.png"))
-        self.stopAction = toolBar.addAction(stopIcon, "Stop")
-        self.stopAction.triggered.connect(self.player.stop)
-        playMenu.addAction(self.stopAction)
+        self._stop_action = tool_bar.addAction(stopIcon, "Stop")
+        self._stop_action.triggered.connect(self._ensure_stopped)
+        play_menu.addAction(self._stop_action)
 
         # many lines were omitted
 
index 0bfd9e2768a7fce7c6a3dff8ca7f2d05b71f7b3d..1b0d1c809a9ad63a31e27a15e49d3e3a73de883f 100644 (file)
@@ -1,10 +1,12 @@
-.. _signals-and-slots:
+.. _tutorial_signals_and_slots:
+
 
 Signals and Slots
 =================
 
-Due to the nature of Qt, ``QObject``\s require a way to communicate, and that's
-the reason for this mechanism to be a **central feature of Qt**.
+Due to the nature of Qt, :class:`~PySide6.QtCore.QObject`\s
+require a way to communicate, and that's the reason for this mechanism to
+be a **central feature of Qt**.
 
 In simple terms, you can understand **Signal and Slots** in the same way you
 interact with the lights in your house. When you move the light switch
@@ -25,8 +27,9 @@ etc.
     there are essential differences that make it an unintuitive approach,
     like ensuring the type correctness of callback arguments, and some others.
 
-All classes that inherit from ``QObject`` or one of its subclasses, like
-``QWidget`` can contain signals and slots. **Signals are emitted by objects**
+All classes that inherit from :class:`~PySide6.QtCore.QObject` or one of its
+subclasses, like :class:`~PySide6.QtWidgets.QWidget`, can contain signals and
+slots. **Signals are emitted by objects**
 when they change their state in a way that may be interesting to other objects.
 This is all the object does to communicate. It does not know or care whether
 anything is receiving the signals it emits. This is true information
@@ -43,12 +46,14 @@ a signal directly to another signal. (This will emit the second signal
 immediately whenever the first is emitted.)
 
 Qt's widgets have many predefined signals and slots. For example,
-``QAbstractButton`` (base class of buttons in Qt) has a ``clicked()``
-signal and ``QLineEdit`` (single line input field) has a slot named
-``clear()``. So, a text input field with a button to clear the text
-could be implemented by placing a ``QToolButton`` to the right of the
-``QLineEdit`` and connecting its ``clicked()`` signal to the slot
-``clear()``. This is done using the ``connect()`` method of the signal:
+:class:`~PySide6.QtWidgets.QAbstractButton` (base class of buttons in Qt)
+has a ``clicked()`` signal and :class:`~PySide6.QtWidgets.QLineEdit`
+(single line input field) has a slot named ``clear()``.
+So, a text input field with a button to clear the text
+could be implemented by placing a :class:`~PySide6.QtWidgets.QToolButton`
+to the right of the ``QLineEdit`` and connecting its ``clicked()`` signal to the slot
+``clear()``. This is done using the :meth:`~PySide6.QtCore.Signal.connect`
+method of the signal:
 
 .. code-block:: python
 
@@ -56,8 +61,10 @@ could be implemented by placing a ``QToolButton`` to the right of the
     line_edit = QLineEdit()
     button.clicked.connect(line_edit.clear)
 
-``connect()`` returns a ``QMetaObject.Connection`` object, which can be
-used with the ``disconnect()`` method to sever the connection.
+:meth:`~PySide6.QtCore.Signal.connect` returns a
+:class:`~PySide6.QtCore.QMetaObject.Connection` object, which can be
+used with the :meth:`~PySide6.QtCore.Signal.disconnect` method to sever
+the connection.
 
 Signals can also be connected to free functions:
 
@@ -81,13 +88,17 @@ designed in the
 `Signal-Slot Editor <https://doc.qt.io/qt-6/designer-connection-mode.html>`_
 of *Qt Widgets Designer*.
 
+The :meth:`~PySide6.QtCore.Signal.connect` function takes an optional parameter
+of :class:`~PySide6.QtCore.Qt.ConnectionType` that specifies the behavior
+with regards to threads and event loops.
+
 The Signal Class
 ----------------
 
 When writing classes in Python, signals are declared as class level
-variables of the class ``QtCore.Signal()``. A QWidget-based button
-that emits a ``clicked()`` signal could look as
-follows:
+variables of the class :class:`~PySide6.QtCore.Signal`.
+A :class:`~PySide6.QtWidgets.QWidget`-based button that emits a
+``clicked()`` signal could look as follows:
 
 .. code-block:: python
 
@@ -146,8 +157,8 @@ The Slot Class
 --------------
 
 Slots in QObject-derived classes should be indicated by the decorator
-``@QtCore.Slot()``. Again, to define a signature just pass the types
-similar to the ``QtCore.Signal()`` class.
+:deco:`~PySide6.QtCore.Slot`. Again, to define a signature just pass the types
+similar to the :class:`~PySide6.QtCore.Signal` class.
 
 .. code-block:: python
 
@@ -163,9 +174,9 @@ nothing is passed as name then the new slot will have the same name as the
 function that is being decorated.
 
 We recommend marking all methods used by signal connections with a
-``@QtCore.Slot()`` decorator. Not doing causes run-time overhead due to the
-method being added to the ``QMetaObject`` when creating the connection. This is
-particularly important for ``QObject`` classes registered with QML, where
+:deco:`~PySide6.QtCore.Slot` decorator. Not doing causes run-time overhead
+due to the method being added to the ``QMetaObject`` when creating the connection.
+This is particularly important for ``QObject`` classes registered with QML, where
 missing decorators can introduce bugs.
 
 Missing decorators can be diagnosed by setting activating warnings of the
@@ -260,4 +271,4 @@ when introspecting ``QMetaObject``:
     for m in range(mo.methodOffset(), mo.methodCount()):
         print(mo.method(m).methodSignature())
 
-Slots should be decorated using :ref:`@Slot <slot-decorator>`.
+Slots should be decorated using :deco:`~PySide6.QtCore.Slot`.
index 5c04529fd48aeced3d50d4141ef0d65df921c92c..ecaedc27e965772f44ffbb609ebd381918ca2812 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_tablewidget:
+
+
 Displaying Data Using a Table Widget
 ====================================
 
index 21c16cdcdc5cce88e69aab33df1ba09010223d9b..803964c23ffc55fd7c4e9b58663637ebb787c5a0 100644 (file)
@@ -1,4 +1,5 @@
-.. _translations:
+.. _tutorial_translations:
+
 
 Translating Applications
 ========================
@@ -12,7 +13,7 @@ Qt Linguist
 `Qt Linguist`_ and
 its related tools can be used to provide translations for applications.
 
-The :ref:`qt-linguist-example` example illustrates this. The example is
+The :ref:`example_widgets_linguist` example illustrates this. The example is
 very simple, it has a menu and shows a list of programming languages with
 multiselection.
 
@@ -78,7 +79,7 @@ the files are converted to a binary form (``.qm`` files):
 
 To avoid having to ship the ``.qm`` files, it is recommend
 to put them into a Qt resource file along with icons and other
-applications resources (see :ref:`using_qrc_files`).
+applications resources (see :ref:`tutorial_qrcfiles`).
 The resource file ``linguist.qrc`` provides the ``example_de.qm``
 under ``:/translations``:
 
@@ -120,7 +121,7 @@ GNU gettext
 The `GNU gettext`_ module
 can be used to provide translations for applications.
 
-The :ref:`gettext-example` example illustrates this. The example is
+The :ref:`example_widgets_gettext` example illustrates this. The example is
 very simple, it has a menu and shows a list of programming languages with
 multiselection.
 
index f431cb5c46f63560c911a6bf4aa606240591161e..da62abff75bf5f651ff755339ac83831c26685b7 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_treewidget:
+
+
 Displaying Data Using a Tree Widget
 ===================================
 
index cb945908d186906eaa0945c75c3ce3d1e6a44490..32d32300ce8ffd85b7331f9a8113cca6adb6a3e6 100644 (file)
@@ -1,4 +1,5 @@
-.. _using_ui_files:
+.. _tutorial_uifiles:
+
 
 Using ``.ui`` files from Designer or QtCreator with ``QUiLoader`` and ``pyside6-uic``
 *************************************************************************************
@@ -222,7 +223,7 @@ Qt for Python provides a simple interface for this which is similar to
 
 The widget needs to be provided as a Python module, as shown by
 the :ref:`widgetbinding-example` (file ``wigglywidget.py``) or
-the :ref:`task-menu-extension-example` (file ``tictactoe.py``).
+the :ref:`example_designer_taskmenuextension` (file ``tictactoe.py``).
 
 Registering this with *Qt Widgets Designer* is done by providing
 a registration script named ``register*.py`` and pointing
index ef14c7e9958c508ce2390cee261e48fb2d134809..f73bd72b74fb2eac2053d76f47acc46ed0bf6c39 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_widgets:
+
+
 Your First QtWidgets Application
 *********************************
 
@@ -49,4 +52,3 @@ After the creation of the application object, we have created a
 Finally, we call `app.exec()` to enter the Qt main loop and start
 to execute the Qt code. In reality, it is only here where the label
 is shown, but this can be ignored for now.
-
index 106483b7beec6d1060c5081041e7c48bd5ca51b0..a17824b060406cee3890cfcd043e2b2b8e2e9fce 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 2fa51c0a8b44879fa58334cf757b416a11f00c43..8f9e3ae960671cc129afdfc0ebaaf516b65a61e7 100644 (file)
@@ -1,4 +1,5 @@
-.. _widgetstyling:
+.. _tutorial_widgetstyling:
+
 
 Styling the Widgets Application
 ===============================
index 59a9d9ee0771f9cf8b27f833dc67986a5bc2e364..acffce40d9eedaf5e67595f229a0c379068649f0 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_add_chart:
+
+
 Chapter 5 - Add a chart view
 =============================
 
@@ -16,5 +19,3 @@ previous chapter to add a QChartView:
    :linenos:
    :lines: 3-
    :emphasize-lines: 2-3,6,22-36,47-49
-
-
index 623372a075d18fb1af4497996b786835615c133e..ab5468cd67086acedd337f7d547dd33817e3ca26 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_add_mainwindow:
+
+
 Chapter 3 - Create an empty QMainWindow
 ==========================================
 
index f658640bfd89c53bddb5d1b85616aab4979224ee..5b7e5e735abb57962de39d9dd30600ffa1c15c3e 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_add_tableview:
+
+
 Chapter 4 - Add a QTableView
 =============================
 
@@ -67,4 +70,3 @@ In the following snippets you'll see those changes highlighted:
    :linenos:
    :lines: 3-
    :emphasize-lines: 46-47
-
index 995b9906ae147eeff2d0652f778468d193d46f4d..14a835449f6d5660712d75a0d03d3e4e69649976 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import argparse
 import pandas as pd
@@ -15,4 +16,3 @@ if __name__ == "__main__":
     args = options.parse_args()
     data = read_data(args.file)
     print(data)
-
index 9ea96097cf5d1dcf07d0a5f298bc8882aa9a1e09..5f23afbbe362101ab7f69b6f9c476b693df4f191 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import argparse
 import pandas as pd
@@ -38,4 +39,3 @@ if __name__ == "__main__":
     args = options.parse_args()
     data = read_data(args.file)
     print(data)
-
index bbf85e17df9c6210843a125f8969c0e49bcd3519..ca2c78738d05828d577ff6a57464405effb7754a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import argparse
@@ -48,4 +49,3 @@ if __name__ == "__main__":
     window.show()
 
     sys.exit(app.exec())
-
index 745f2fefedf118c28a5ed68aafcbd8c59d822702..8ce8f9ace1abcd05f50d625063ef5405f8248917 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtGui import QAction, QKeySequence
index 87b962bd327682175b8e37e4f1a46a4575c5f2ef..99abd360602616fb521e56afab638c92589506f9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import argparse
@@ -50,4 +51,3 @@ if __name__ == "__main__":
     window.show()
 
     sys.exit(app.exec())
-
index 2a2bfeb09274a3b3facb35272f5657f9b238d024..85e24833f514c24440816546d5ba768f361d3c31 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import (QHBoxLayout, QHeaderView, QSizePolicy,
                                QTableView, QWidget)
@@ -21,23 +22,18 @@ class Widget(QWidget):
         # QTableView Headers
         self.horizontal_header = self.table_view.horizontalHeader()
         self.vertical_header = self.table_view.verticalHeader()
-        self.horizontal_header.setSectionResizeMode(
-                               QHeaderView.ResizeToContents
-                               )
-        self.vertical_header.setSectionResizeMode(
-                             QHeaderView.ResizeToContents
-                             )
+        self.horizontal_header.setSectionResizeMode(QHeaderView.ResizeToContents)
+        self.vertical_header.setSectionResizeMode(QHeaderView.ResizeToContents)
         self.horizontal_header.setStretchLastSection(True)
 
         # QWidget Layout
         self.main_layout = QHBoxLayout()
         size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
 
-        ## Left layout
+        # Left layout
         size.setHorizontalStretch(1)
         self.table_view.setSizePolicy(size)
         self.main_layout.addWidget(self.table_view)
 
         # Set the layout to the QWidget
         self.setLayout(self.main_layout)
-
index ebe2997a105e9d5e2547956136e998cc3a37ef70..ded7fdf5cd0a1c3859bfacb9c7480c05f94d5b10 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
-from PySide6.QtCore import Slot
 from PySide6.QtGui import QAction, QKeySequence
 from PySide6.QtWidgets import QMainWindow
 
@@ -15,7 +15,7 @@ class MainWindow(QMainWindow):
         self.menu = self.menuBar()
         self.file_menu = self.menu.addMenu("File")
 
-        ## Exit QAction
+        # Exit QAction
         exit_action = QAction("Exit", self)
         exit_action.setShortcut(QKeySequence.Quit)
         exit_action.triggered.connect(self.close)
@@ -29,4 +29,3 @@ class MainWindow(QMainWindow):
         # Window dimensions
         geometry = self.screen().availableGeometry()
         self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
-
index 08eeeeed6f3d8741e182e42233e689e080f3e98a..cc2ac12ab4837945e1d331ddfca97aae97edcffa 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
 from PySide6.QtGui import QColor
@@ -48,4 +49,3 @@ class CustomTableModel(QAbstractTableModel):
             return Qt.AlignRight
 
         return None
-
index 87b962bd327682175b8e37e4f1a46a4575c5f2ef..99abd360602616fb521e56afab638c92589506f9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import argparse
@@ -50,4 +51,3 @@ if __name__ == "__main__":
     window.show()
 
     sys.exit(app.exec())
-
index 15b7e97e54818df80006b8a2f12118a48c243205..40954ea3477e4b86beac28f00761785548fb1ca2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QDateTime, Qt
 from PySide6.QtGui import QPainter
index ed12c1f8f35f1973ea065a142b66e869eaa56c2b..ded7fdf5cd0a1c3859bfacb9c7480c05f94d5b10 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
-from PySide6.QtCore import Slot
 from PySide6.QtGui import QAction, QKeySequence
 from PySide6.QtWidgets import QMainWindow
 
@@ -29,4 +29,3 @@ class MainWindow(QMainWindow):
         # Window dimensions
         geometry = self.screen().availableGeometry()
         self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
-
index 08eeeeed6f3d8741e182e42233e689e080f3e98a..cc2ac12ab4837945e1d331ddfca97aae97edcffa 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
 from PySide6.QtGui import QColor
@@ -48,4 +49,3 @@ class CustomTableModel(QAbstractTableModel):
             return Qt.AlignRight
 
         return None
-
index 2d851cc1406d285ca67237051d2930a6f1771040..ffc81d89c51ecf34eac45bbe98decda76e628ac3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import argparse
index cbcc126a1cde9179bd2c46fc447dff5e9d80a3e8..f987689eab7b4bc9045f54b2326797d8f42ad021 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import QDateTime, Qt
 from PySide6.QtGui import QPainter
@@ -92,4 +93,3 @@ class Widget(QWidget):
         # Getting the color from the QChart to use it on the QTableView
         color_name = self.series.pen().color().name()
         self.model.color = f"{color_name}"
-
index 116e53dc36e611af70a1741b11ab55a41355eb05..21842cff10bb4e1efff9423293303aa90e800e76 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Slot
 from PySide6.QtGui import QAction, QKeySequence, QScreen
index 4952c0c389458ec546f4ee9780d36556b2db6fc4..3201e5887058217a90d36f4c9466cb436b136168 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
 from PySide6.QtGui import QColor
index edfac386261abb531276684fd6352f3849036aef..bef134e5b695a422888e044b3c0758503440e0c0 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_filter_data:
+
+
 Chapter 2 - Filtering data
 ===========================
 
index 35b56706ae19b72d9dd6a9278522a1d3b407c97c..6adf845441a70d18b34819ec05ec6b7e65e7863c 100644 (file)
@@ -1,3 +1,6 @@
+.. _datavisualize_index:
+
+
 Data Visualization Tool Tutorial
 *********************************
 
index f71fca4c8488920e1ffc1a4e72114af61d570122..e4374e8613f9d0ce99e31023496ede9e48d3a017 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_plot_datapoints:
+
+
 Chapter 6 - Plot the data in the ChartView
 ===========================================
 
index 53b0d1cb05236f13c92c16f751ff78d9b85a65ed..8be0e1c2f0d16ec469bafa8e289c3830dae6a15c 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_read_data:
+
+
 Chapter 1 - Reading data from a CSV
 ===================================
 
index 85c32634708257f4b7128e55923b46c024f786e7..a975ea74e91cea749973c010272c527ed864bcde 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_mixed_debugging:
+
+
 How To Debug a C++ Extension of a PySide6 Application?
 ******************************************************
 
@@ -100,8 +103,7 @@ Add the **bin** and **lib** path to the environment variables
 Build PySide6 using the Qt 6 that you built earlier
 ----------------------------------------------------
 
-Follow the steps mentioned `Getting Started - Qt for Python
-<https://doc.qt.io/qtforpython/gettingstarted/index.html>`_
+Follow the steps mentioned in :doc:`Building from Source <../../building_from_source/index>` guide.
 
 You may manually select the modules to install using the ``--module-subset`` cli
 argument for `setup.py`. This was my installation script
index 223e608fc278b5e3e41b82ace4148f205c85574e..bf84fecc74114fcfd52ad0738a46f1d9c992ec76 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_qml_debugging:
+
+
 Using Qt Creator's QML Debugger for a PySide6 QML Application
 *************************************************************
 
index a78a67fad7a75b1e561311ae8afe6798c3711d00..dd10de352b6f22da0eea70f1114d2e5752b76b69 100644 (file)
@@ -36,4 +36,3 @@ You will now hit you breakpoint and can start debugging your code.
 .. image:: breakpoint_cpp.png
     :alt: breakpoint cpp
     :align: center
-
diff --git a/sources/pyside6/doc/tutorials/embedded/boot2qt.md b/sources/pyside6/doc/tutorials/embedded/boot2qt.md
new file mode 100644 (file)
index 0000000..f92f05a
--- /dev/null
@@ -0,0 +1,176 @@
+(tutorial_boottoqt_raspberrypi)=
+
+# Deploying a PySide6 Application to Boot to Qt on Raspberry Pi
+
+This tutorial provides a step-by-step guide to setting up Boot to Qt on a Raspberry Pi and deploying
+a PySide6 application. You will learn how to download and flash the Boot to Qt image, create a
+sample PySide6 application, and manually deploy it to the Raspberry Pi. Additionally, the tutorial
+covers automating the deployment process with a shell script and offers tips for improving the
+manual deployment steps.
+
+> **Note**: This tutorial was tested on Unix hosts. Windows hosts might not work as expected.
+
+> **Note**: Boot to Qt comes with PySide6 pre-installed in its Python environment, so you can start
+> developing and deploying PySide6 applications right away without needing to install additional
+> packages. Each first minor release of Qt for Device Creation (eg: 6.8.0) does not include PySide6.
+> The further sub-releases (eg: 6.8.1, 6.8.2) includes PySide6.
+
+## Prerequisites
+
+- Raspberry Pi 4/5 (aarch64)
+- SD card to flash the Boot to Qt image
+- `ssh` and `scp` tools available
+- Network connectivity between your computer and the Raspberry Pi
+- Read the [Boot to Qt Quick Start for Raspberry Pi][boot2qt-quick-start] guide
+
+## Setting Up Boot to Qt on Raspberry Pi
+
+1. **Download Boot to Qt Image**
+   - Obtain the Boot to Qt image for Raspberry Pi as mentioned using the Qt maintenance tool in
+     [Installing Boot to Qt Software Stack][installing-boot2qt]
+   - Ensure the image you downloaded has PySide6 pre-installed in the Python environment.
+
+2. **Flash the Image to an SD Card**
+   - Use Qt Creator to flash the Boot to Qt image to an SD card by following the instructions in
+     [Flashing the Image to an SD Card][flashing-image]
+   - Alternatively, use a tool like `balenaEtcher` or `dd` to write the image download in step 1 to
+     the SD card.
+   - **Using balenaEtcher:**
+     1. Download and install balenaEtcher from [balena.io][balenaetcher].
+     2. Open balenaEtcher.
+     3. Select the Boot to Qt image file.
+     4. Select the target SD card.
+     5. Click "Flash!" to start the process.
+   - **Using `dd`:**
+     1. Identify the SD card device name using `lsblk` or `fdisk -l`.
+     2. Unmount the SD card partitions.
+     3. Write the image to the SD card using `dd`:
+
+        ```sh
+        sudo dd if=<path-to-image-file> of=/dev/sdX bs=4M status=progress
+        ```
+     > **Warning:** Ensure you replace `/dev/sdX` with the correct device name of the SD card.
+
+3. **Boot the Raspberry Pi**
+   - Insert the newly flashed SD card into the Raspberry Pi.
+   - Power on your device (make sure you are connected to the network)
+
+4. **Verify Boot to Qt is Running**
+   - Ensure the Raspberry Pi boots into Boot to Qt. The device starts with Boot to Qt Demo Launcher
+     application by default. The IP address is shown if the device is connected to an Ethernet or
+     Wi-Fi network, or to the host computer via USB OTG.
+   - You can check network connectivity by pinging the device.
+
+## Preparing the PySide6 Application
+
+1. **Create a Sample PySide6 Application**
+   - Write a simple PySide6 application:
+
+     ```python
+     # sample_app.py
+     import sys
+     from PySide6.QtWidgets import QApplication, QLabel
+
+     app = QApplication(sys.argv)
+     label = QLabel("Hello from PySide6 on Boot to Qt!")
+     label.show()
+     sys.exit(app.exec())
+     ```
+
+2. **Test Locally**
+   - Run the application on your development machine to ensure it works.
+
+     ```sh
+     python sample_app.py
+     ```
+    > **Note:** Make sure you have PySide6 installed on your local environment. If not, you can install
+    > it using `pip`: `pip install PySide6`.
+
+
+## Deploying the Application to Boot to Qt
+
+1. **Find the Raspberry Pi's IP Address**
+   - For finding the IP Address of the device, refer to the section `Verify Boot to Qt is Running`.
+
+2. **Transfer the Application Using `scp`**
+   - Copy the application to the Raspberry Pi:
+
+     ```sh
+     scp sample_app.py username@<RASPBERRY_PI_IP>:
+     ```
+    > **Note:** This command copies the file to the home directory of the user `username`. You can
+    > specify a different directory if needed.
+
+3. **SSH into the Raspberry Pi**
+   - Connect to the Raspberry Pi via SSH:
+
+     ```sh
+     ssh username@<RASPBERRY_PI_IP>
+     ```
+
+4. **Run the Application on the Raspberry Pi**
+   - Execute the application:
+
+     ```sh
+     python3 /home/username/sample_app.py
+     ```
+
+## Automating Deployment with a Shell Script
+
+You can streamline the deployment process with a shell script.
+
+```sh
+#!/bin/bash
+
+# Variables
+APP_NAME="sample_app.py"
+PI_USER="username"
+PI_IP="<RASPBERRY_PI_IP>"
+REMOTE_PATH="/home/username/"
+
+# Copy the application to the Raspberry Pi
+scp $APP_NAME $PI_USER@$PI_IP:$REMOTE_PATH
+
+# Run the application on the Raspberry Pi
+ssh $PI_USER@$PI_IP "python3 $REMOTE_PATH$APP_NAME"
+```
+
+- **Make the Script Executable**
+
+  ```sh
+  chmod +x deploy.sh
+  ```
+
+- **Run the Script**
+
+  ```sh
+  ./deploy.sh
+  ```
+
+## Improving the Manual Deployment Steps
+
+Although SSH and SCP are effective, you can enhance the deployment process:
+
+- **Use SSH Key Authentication**
+  - Set up SSH keys to avoid entering passwords each time and use `ssh-copy-id` tool to copy the
+    public key to the Raspberry Pi.
+
+- **Use `rsync` for Efficient File Transfer**
+  - Alternative to `scp` for transferring files.
+  - `rsync` only transfers changed files.
+
+    ```sh
+    rsync -avz sample_app.py username@<RASPBERRY_PI_IP>:
+    ```
+    > **Note:** This command copies the file to the home directory of the user `username`.
+
+## Conclusion
+
+In this tutorial, we have provided a comprehensive guide to setting up Boot2Qt on a Raspberry Pi and
+deploying a PySide6 application.
+
+[boot2qt-quick-start]: https://doc.qt.io/Boot2Qt/b2qt-qsg-raspberry.html
+[installing-boot2qt]: https://doc.qt.io/Boot2Qt/b2qt-qsg-raspberry.html#installing-boot-to-qt-software-stack-using-qt-maintenance-tool
+[flashing-image]: https://doc.qt.io/Boot2Qt/b2qt-qsg-raspberry.html#installing-boot-to-qt-on-the-target-device
+[qt-creator]: https://doc.qt.io/qtcreator
+[balenaetcher]: https://etcher.balena.io/
diff --git a/sources/pyside6/doc/tutorials/embedded/boot2qt_launcher.png b/sources/pyside6/doc/tutorials/embedded/boot2qt_launcher.png
new file mode 100644 (file)
index 0000000..8d228fa
Binary files /dev/null and b/sources/pyside6/doc/tutorials/embedded/boot2qt_launcher.png differ
index 2064488ae9a843ad3b47e3589ef5ebbcd88e5201..22723af1fe99ce2e13ade53b8d0a3e417dc7b734 100644 (file)
@@ -1,3 +1,6 @@
+.. _tutorial_expenses:
+
+
 Expenses Tool Tutorial
 ======================
 
index a3a99847080fbfe03334b2dc74d001c5376267fc..eefe5192ac22fecf9e65061553ca858592e15c74 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import Qt, Slot
index 05554440975ee5e7aecbd039084944f5ed9667a6..0817fae250bd0b8ec21896b56622cc6db5dc0c41 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import QMargins, Qt, Slot, QSize
index 27f2aef6503914f5fbf2fee9321b3d11ea907b8e..917d069bdebf65be747ab673896d7daad6d63ab8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import QApplication, QMainWindow
index 039b818df70d5118b25e5bbaeefe007f685297ae..e82b5e76b8963174bf970429f1d7af9f067fb6a3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import QApplication, QMainWindow
index 039b818df70d5118b25e5bbaeefe007f685297ae..e82b5e76b8963174bf970429f1d7af9f067fb6a3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import QApplication, QMainWindow
index 6723690a81b01103b3b8183bf06ef15ee0382dca..6bf5ad8cc8365007c9ad17db41f9c1119d6b2f9e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import QApplication, QMainWindow, QWidget
index df0362fde5f1ffcf0603f4a80d26d23f1b9b1804..dc111d75dca529fe9e1b8bf3b7d8843b7935fdd6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout,
index d19a6220f86a6a5fb6b59b26e21164f3ba8eefe8..f905e980e65b7e2693114c66b8998c5830b81d74 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import (QApplication, QFormLayout, QHeaderView,
index b1ed3fecccf6cdda411534158b3298249cffe40f..56634ae0aa15332b06392e56ae6eaa856de88414 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import Slot
index 0e7e21a329a9ba07d5f84abfd795b94d5fe473a5..45b37a67187a8ef0c24caf989ddfa68a8de09af1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import Slot
index 279a4651284913e48cbd72862af3ceee5a897065..99362817d1b6ac5c56e334a9ea28170be187cf46 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import Slot
index a3a99847080fbfe03334b2dc74d001c5376267fc..eefe5192ac22fecf9e65061553ca858592e15c74 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtCore import Qt, Slot
index 688147726571ae01968bdc65fe174c34e0a023f1..f317fb48a7818f64c0cca0f96fdd23c4677ce8f9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtWidgets import QFileSystemModel
 from PySide6.QtQuick import QQuickTextDocument
index 0ac7bec18a16a1b623ff670f36ba0f62fe9be552..44fcf8eb4941cdebe4ef1b972ce86f967ef96da5 100644 (file)
@@ -1,7 +1,9 @@
+(tutorial_extendedexplorer)=
+
 # Extending the file system explorer example
 
 This tutorial shows how to extend the
-[Filesystem Explorer Example](filesystemexplorer_example)
+[Filesystem Explorer Example](example_quickcontrols_filesystemexplorer)
 by adding a simple scheme manager. This feature will allow you to switch color
 schemes during the application's runtime. The color schemes will be declared in
 JSON format and made available through a custom Python-QML plugin.
index f1e6e7d933541cc1a011a52af4fca6ae09df22ce..13a1c5e5b605ce18f38896c4c4c91b07b9bf90bb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
@@ -10,8 +11,8 @@ This example shows how to customize Qt Quick Controls by implementing a simple f
 # import FileSystemModule.rc_icons
 # import FileSystemModule.rc_app
 
-from scheme_manager import SchemeManager
-from editormodels import FileSystemModel
+from scheme_manager import SchemeManager  # noqa: F401
+from editormodels import FileSystemModel  # noqa: F401
 import PySide6
 from PySide6.QtGui import QGuiApplication, QIcon
 from PySide6.QtQml import QQmlApplicationEngine
@@ -47,4 +48,6 @@ if __name__ == '__main__':
         fsm = engine.singletonInstance("FileSystemModule", "FileSystemModel")
         fsm.setInitialDirectory(args[0])
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index 8d732093c9907185e69635158f99127b280017b3..f4228a8b4feafdec95279655781b96ea76b11ac4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import json
 from pathlib import Path
diff --git a/sources/pyside6/doc/tutorials/finance_manager/index.md b/sources/pyside6/doc/tutorials/finance_manager/index.md
new file mode 100644 (file)
index 0000000..c9df2e9
--- /dev/null
@@ -0,0 +1,52 @@
+(tutorial_financemanager)=
+
+# Finance Manager Tutorial
+
+## Summary
+
+This tutorial series is structured into three comprehensive parts, each building upon the previous
+one to create a fully functional finance manager application. It demonstrates how PySide6 can be
+used to interact with other popular packages in the Python ecosystem. The series starts with the
+basics of PySide6 and QtQuick, then integrates SQLite for database management using
+[SQLAlchemy], and finally incorporates [FastAPI] and [Pydantic] for server-side operations and
+REST API integration.
+
+### Part 1: Building a Complete PySide6 QtQuick Application
+- **Introduction**: Overview of the tutorial series and prerequisites.
+- **Project Setup**: Setting up the development environment and installing necessary packages.
+- **Creating the Main Window**: Setting up the main window using PySide6 and
+    creating the main QML file.
+- **Adding Basic UI Components**: Adding buttons, labels, input fields, listview, Pie chart and
+    handling user interactions with PySide6 and QML.
+- For more details, see {ref}`tutorial_financemanager_partone`.
+
+### Part 2: Integrating SQLite Database with SQLAlchemy
+- **Setting Up SQLAlchemy**: Installing SQLAlchemy and configuring the SQLite database connection.
+- **Creating Database Models**: Defining database models for finance data.
+- **CRUD Operations**: Implementing part of the CRUD operations through Create, and Read operations
+  and connecting them to the PySide6 application.
+- For more details, see {ref}`tutorial_financemanager_parttwo`.
+
+### Part 3: Using FastAPI and Pydantic for Server-Side Operations
+- **Setting Up FastAPI**: Installing FastAPI and Uvicorn, and creating a basic FastAPI application.
+- **Creating REST Endpoints**: Defining RESTful endpoints for finance data and implementing CRUD
+  operations through the API.
+- **Connecting FastAPI with PySide6**: Making HTTP requests from the PySide6 application and
+  displaying data fetched from the API in the UI.
+- For more details, see {ref}`tutorial_financemanager_partthree`.
+
+Each part ends with instructions on how to deploy the application using {ref}`pyside6-deploy`.
+This structured approach ensures that readers can follow along and understand each concept before
+moving on to the next one, resulting in a robust and scalable finance manager application.
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
+
+```{toctree}
+:maxdepth: 1
+:hidden:
+
+part1/part1.md
+part2/part2.md
+part3/part3.md
diff --git a/sources/pyside6/doc/tutorials/finance_manager/part1/part1.md b/sources/pyside6/doc/tutorials/finance_manager/part1/part1.md
new file mode 100644 (file)
index 0000000..6f8b87d
--- /dev/null
@@ -0,0 +1,400 @@
+(tutorial_financemanager_partone)=
+
+# Finance Manager Tutorial - Part 1
+
+In this tutorial, we will create a finance manager app using QtQuick and PySide6. The app will allow
+you to add your expenses and visualize them using a pie charts based on the category of spending.
+The application is designed in a way that it is compatible with Desktop and Android platforms.
+
+![Finance Manager GIF](resources/finance_manager.gif)
+
+![Finance Manager Android](resources/finance_manager_android.gif)
+
+To download the complete source code for this tutorial, visit
+{ref}`example_tutorials_finance_manager_part1`.
+
+## Prerequisites
+
+Before we begin, firstly make sure you have Python 3.9+ and PySide6 installed within you Python
+environment. You can install it using pip:
+
+```bash
+pip install PySide6
+```
+
+## Project Design
+
+The finance manager app is a simple app demonstrating the use of PySide6 to integrate QtQuick with
+Python, allowing for a seamless combination of QML for the user interface and Python for the backend
+logic. It will have the following components:
+
+1. Expense List: This list will display all the entered expenses, showing the expense name, amount,
+   category and date. The expenses are organized by their month and year.
+
+2. PieChart: This chart will visualize the expenses based on their categories, giving users a clear
+   overview of their spending habits.
+
+3. Add Expense: A dialog enabling the user to add new expenses.
+
+The overall project structure will be as follows:
+
+```
+finance_manager/
+├── main.py
+├── financemodel.py
+├── Finance/
+│   â”œâ”€â”€ Main.qml
+│   â”œâ”€â”€ FinanceView.qml
+│   â”œâ”€â”€ FinanceDelegate.qml
+│   â”œâ”€â”€ FinancePieChart.qml
+│   â”œâ”€â”€ AddDialog.qml
+│   â””── qmldir
+```
+## Let's get started!
+
+### Component Overview
+
+In the first part of this tutorial, we will start by creating the Expense List using some
+pre-defined expenses. For this, we will create a new Python file `financemodel.py` that
+defines a class **FinanceModel** that will be used to manage the expenses from Python and expose
+it to QML.
+
+<details>
+<summary class="prominent-summary">financemodel.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/financemodel.py
+---
+language: python
+caption: FinanceModel class definition
+linenos: true
+---
+```
+</details>
+
+Here's a brief overview of the `FinanceModel` class, its components and methods:
+
+1. **QML type registration**
+   - The `FinanceModel` class is registered as a QML type using the {deco}`~PySide6.QtQml.QmlElement`
+   decorator. This decorator is used to define a Python class as a QML type, allowing it to be used
+   in QML files.
+   - The `QML_IMPORT_NAME` variable is used to define the name of the module that will be imported
+    in QML to access the `FinanceModel` class.
+2. **Members**
+    - **FinanceRole Enum**: Defines custom roles for the model data, such as `ItemNameRole`,
+        `CategoryRole`, `CostRole`, `DateRole` and `MonthRole`.
+    - **Finance Dataclass**: Represents an individual expense with the attributes `item_name`,
+        `category`, `cost`, `date` and `month`.
+    - **init Method**: Initializes the model with some pre-defined expenses.
+    - **rowCount Method**: Returns the number of items in the model.
+    - **data Method**: Returns the data for a given role and index in the model.
+    - **getCategoryData Method**: Returns a dictionary the total cost for each category in the
+      model. This method has the `@Slot` decorator to make it accessible from QML.
+    - **roleNames Method**: Maps role names to their `QByteArray` values.
+    - **get Method**: A `@Slot` method to get the expense data at a given index.
+    - **append Method**: A `@Slot` method to append a new expense to the model.
+
+For using as a data model in the `ListView` component in QML, the methods `rowCount`, `data`, and
+`roleNames` are required.
+
+Now that we have defined the `FinanceModel` class, let's create the QML components to display the
+expenses. First, we create `Finance/Main.qml` file that will be the main QML file for our app.
+
+<details>
+<summary class="prominent-summary">Main.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/Main.qml
+---
+language: QML
+caption: Main.qml
+linenos: true
+---
+```
+</details>
+
+In the `Main.qml` we import the created `Finance` QML module file and the file has the following components:
+
+1. **ApplicationWindow**:
+   - The main window of the application.
+   - Sets the theme to `Material.Dark` and accent color to `Material.Gray`.
+   - Adjusts the window size to the screen dimensions.
+   - Contains the title "Finance Manager".
+
+2. **ToolBar**:
+   - A toolbar that is only visible on mobile platforms (Android and iOS). Note that PySide6
+     supports only Android, but you can use the same code with Qt C++ for iOS.
+   - Contains a `Label` with the text "Finance Manager".
+
+3. **ColumnLayout**:
+   - A layout that arranges its children in a column.
+   - Fills the entire window.
+
+4. **TabBar**:
+   - Contains two `TabButton` components for switching between **Expense** and **Charts** views.
+
+5. **StackLayout**:
+   - A layout that stacks its children on top of each other.
+   - Contains two `Item`components for the "Expenses" and "Charts" views.
+
+6. **FinanceView**:
+   - A custom component to display the list of expenses.
+   - Binds to the `finance_model`.
+   - This component is defined in the `FinanceView.qml` file.
+
+7. **FinancePieChart**:
+   - A custom component to display a pie chart of expenses by category.
+   - Updates the chart with data from `finance_model.getCategoryData()` when the component is
+     completed.
+   - This component is defined in the `FinancePieChart.qml` file.
+
+8. **FinanceModel**:
+   - The data model created from Python to store finance data. This is imported by import the
+     QML module `Finance` in the `Main.qml` file.
+
+9. **AddDialog**:
+   - A dialog for adding new expense entries.
+   - Appends new entries to `finance_model` and updates the pie chart.
+
+10. **RoundButton**:
+    - A circular button to open the `AddDialog.qml`.
+    - Positioned at the bottom-right corner of the window.
+    - Contains a "+" symbol and has a highlighted appearance.
+
+Now that we have the basic structure of the main QML file, let's create the `FinanceView.qml` file:
+
+<details>
+<summary class="prominent-summary">FinanceView.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/FinanceView.qml
+---
+language: QML
+caption: FinanceView.qml
+linenos: true
+---
+```
+</details>
+
+`FinanceView.qml` contains the following components:
+
+1. **ListView**:
+   - The main container for displaying a list of items.
+   - Fills the entire parent container using `anchors.fill: parent`.
+   - Uses the `financeModel` property as its data model.
+
+2. **property var financeModel**:
+   - A property to hold the data model for the list.
+   - This model is expected to be passed from the parent component. In this case, it is passed from
+     the `Main.qml` file.
+
+3. **delegate**:
+   - Defines how each item in the `ListView` should be displayed.
+   - Uses a custom component `FinanceDelegate` to render each item. This component is defined in the
+     `FinanceDelegate.qml` file.
+   - Sets the width of each delegate to match the width of the `ListView`.
+
+4. **model**:
+   - Binds the `ListView` to the `financeModel` property.
+   - The `ListView` will display items based on the data in `financeModel`.
+
+5. **section**:
+   - The section property is used to group the items in the list view based on the month of the
+     expense.
+
+6. **ScrollBar.vertical**:
+   - Adds a vertical scrollbar to the `ListView`.
+   - Ensures that users can scroll through the list if the content exceeds the visible area.
+
+These components together create a scrollable list view for displaying financial data, with each
+item rendered using the `FinanceDelegate` component.
+
+Next, let's create the `FinanceDelegate.qml` file:
+
+<details>
+<summary class="prominent-summary">FinanceDelegate.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/FinanceDelegate.qml
+---
+language: QML
+caption: FinanceDelegate.qml
+linenos: true
+---
+```
+</details>
+
+`FinanceDelegate.qml` contains the following components:
+
+1. **ItemDelegate**:
+   - The root element of the delegate.
+   - Represents a single item in the `ListView`.
+
+2. **RowLayout**:
+   - A layout that arranges its children horizontally.
+   - Contains multiple elements to display different parts of the financial data.
+
+3. **Label (dateLabel)**:
+   - Displays the date of the expense.
+
+4. **ColumnLayout**:
+   - A layout that arranges its children vertically.
+   - Contains labels for the item name and category.
+
+5. **Label (item_name)**:
+   - Displays the name of the item.
+
+6. **Label (category)**:
+   - Displays the category of the transaction.
+
+7. **Item**:
+   - A spacer item to take up the remaining space in the `RowLayout` so that the last label is
+     aligned to the right.
+
+8. **ColumnLayout (cost section)**:
+   - A layout that arranges its children vertically.
+   - Contains labels for the cost description and the actual cost.
+
+9. **Label ("you spent:")**:
+   - Displays the static text "you spent:"
+
+10. **Label (cost)**:
+    - Displays the cost of the transaction.
+
+These components together create a detailed and structured visual representation of each financial
+transaction in the `ListView`, displaying the date, item name, category, and cost in a readable format.
+
+Then we create the `FinancePieChart.qml` file:
+
+<details>
+<summary class="prominent-summary">FinancePieChart.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/FinancePieChart.qml
+---
+language: QML
+caption: FinancePieChart.qml
+linenos: true
+---
+```
+</details>
+
+`FinancePieChart.qml` contains the following components:
+
+1. **Item**:
+   - The root element of the QML file.
+   - Sets the width and height to match the screen dimensions.
+
+2. **GraphsView**:
+   - A container for displaying charts. This was introduced with
+     Qt 6.8 with Qt Graphs module.
+
+3. **PieSeries**:
+   - A series type for creating pie charts. This is also a part of the
+     Qt Graphs module.
+
+4. **Text**
+   - A title for the pie chart.
+
+5. **updateChart(data)**:
+   - A JavaScript function to update the pie chart with new data.
+   - Clears existing slices in the PieSeries.
+   - Iterates over the provided data to create new slices.
+   - Each slice is labeled with the category name and value in euros.
+
+These components together create a responsive pie chart that can be dynamically updated with new
+data.
+
+Finally, we create the `AddDialog.qml` file:
+
+<details>
+<summary class="prominent-summary">AddDialog.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/AddDialog.qml
+---
+language: QML
+caption: AddDialog.qml
+linenos: true
+---
+```
+</details>
+
+`AddDialog.qml` contains the following components:
+
+1. **Dialog**:
+   - Root element for the dialog.: Identifier for the dialog.
+   - `signal finished(...)`: Custom signal emitted when the dialog is accepted. In this case, it
+     is emitted when the user adds a new expense.
+
+3. **ColumnLayout**:
+   - Container for the dialog fields.
+
+4. **TextField**:
+   - Input fields for the item name, category, cost and date.
+
+5. **Function `createEntry()`**:
+   - Clears the form fields.
+   - Sets the dialog title.
+   - Opens the dialog.
+
+6. **Dialog Properties**:
+   - `title: qsTr("Add Finance Item")`: Sets the dialog title.
+   - `standardButtons: Dialog.Ok | Dialog.Cancel`: Adds standard OK and Cancel buttons.
+   - `Component.onCompleted`: Hides the dialog when the component is first completed.
+   - `onAccepted`: Calls the `finished` function with the form data when the dialog is accepted.
+
+7. **Function `adjustDialogPosition`**:
+   - Adjusts the dialog position to move slightly up when the virtual keyboard is shown. This is
+     only applicable for mobile platforms.
+
+### Main Python file
+
+Now that we have created the main QML file and the necessary components, we can run the application
+to see the expense list view in action. Create a new Python file `main.py` and add the following
+code:
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/main.py
+language: python
+caption: main.py
+linenos: true
+---
+```
+</details>
+
+In the `main.py` file, we create a `QApplication` instance, load the `Main.qml` file. The Python
+import statement `from financemodel import FinanceModel` registers the `FinanceModel` class as a QML
+type, allowing it to be used in QML files.
+
+### Running the Application
+
+To run the application, execute the `main.py` file using Python:
+
+```bash
+python main.py
+```
+
+### Deploying the Application
+
+To deploy the application on Desktop, you can use the {ref}`pyside6-deploy` tool. Run the following
+command from the project directory:
+
+```bash
+pyside6-deploy --name FinanceManager
+```
+This will create a standalone executable for the application in the project directory.
+
+For deploying to Android, you can use the {ref}`pyside6-android-deploy` tool. Run the following
+command from the project directory:
+
+```bash
+pyside6-android-deploy --name FinanceManager --wheel-pyside=<path_to_pyside6_wheel>
+                       --wheel-shiboken=<path_to_shiboken_wheel>
+```
+This will create an APK file that can be installed on an Android device in the project directory.
+
+## Summary
+
+In this part of the tutorial, we have created a basic finance manager app, including the expense
+list view categorized by month and year, the pie chart, and the add expense dialog. We have also
+defined the `FinanceModel` class in Python to manage the financial data and expose it to QML.
+In the next part of the tutorial, we will continue to build on this foundation by moving the
+expense data into a database based on the [`sqlalchemy`](https://www.sqlalchemy.org/) Python
+package.
diff --git a/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.gif b/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.gif
new file mode 100644 (file)
index 0000000..7f71a15
Binary files /dev/null and b/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.gif differ
diff --git a/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.webp b/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.webp
new file mode 100644 (file)
index 0000000..f8cafa2
Binary files /dev/null and b/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager.webp differ
diff --git a/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager_android.gif b/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager_android.gif
new file mode 100644 (file)
index 0000000..0561bd3
Binary files /dev/null and b/sources/pyside6/doc/tutorials/finance_manager/part1/resources/finance_manager_android.gif differ
diff --git a/sources/pyside6/doc/tutorials/finance_manager/part2/part2.md b/sources/pyside6/doc/tutorials/finance_manager/part2/part2.md
new file mode 100644 (file)
index 0000000..d8de3a4
--- /dev/null
@@ -0,0 +1,116 @@
+(tutorial_financemanager_parttwo)=
+
+# Finance Manager Tutorial - Part 2
+
+In this part of the tutorial, we will extend our finance manager app to store the expenses in a
+SQLite database using the [SQLAlchemy] Python package. This will allow us to
+persist the data and retrieve it even after the application is closed.
+
+To download the complete source code for this tutorial, visit
+{ref}`example_tutorials_finance_manager_part2`.
+
+## Prerequisites
+
+Before we begin, make sure you have [SQLAlchemy] installed within your Python environment.
+
+You can install it using pip:
+
+```bash
+pip install sqlalchemy
+```
+
+## Project Structure
+
+The overall project structure is the same as in the [previous part](tutorial_financemanager_partone)
+of the tutorial.
+
+## Let's Get Started!
+
+### Creating the Database
+
+The first step is to create a `database.py` that takes care of creating and initializing the
+database. This Python code will define the database schema and provide a session object to interact
+with the database. The database is initialized with a single table named `finances` with the same
+initial data we used in the [part one](tutorial_financemanager_partone) of the tutorial.
+
+<details>
+<summary class="prominent-summary">database.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part2/database.py
+---
+language: python
+caption: database.py
+linenos: true
+---
+```
+</details>
+
+> Note: The database called `finances.db` will be created in the directory specified by the
+`FINANCE_MANAGER_DB_PATH` environment variable if it is set. If the environment variable is not set,
+the database will be created in the appropriate application data directory based on the operating
+system.
+
+### Updating the FinanceModel Class
+
+Next, we need to update the FinanceModel class to interact with the database.
+
+In `financemodel.py`, make the following highlighted changes to load the existing expenses from the
+database into the model, and also save new expenses to the database.
+
+<details>
+<summary class="prominent-summary">financemodel.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part2/financemodel.py
+---
+language: python
+caption: Finance model class definition
+linenos: true
+emphasize-lines: 12, 40-50, 93-101
+---
+```
+
+## Updating the Main Application
+
+Finally, we need to update the `main.py` file to initialize the database and use the `FinanceModel`
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part2/main.py
+---
+language: python
+caption: main.py
+linenos: true
+emphasize-lines: 11, 15
+---
+```
+</details>
+
+The rest of the code remains the same as in the previous part of the tutorial.
+
+### Running the Application
+
+To run the application, execute the `main.py` file using Python:
+
+```bash
+python main.py
+```
+
+### Deploying the Application
+
+To deploy the application, follow the same steps as in the
+[previous part](tutorial_financemanager_partone) of the tutorial.
+
+## Summary
+
+In this part of the tutorial, we have extended the finance manager app by integrating a database
+using [SQLAlchemy]. This allows us to store the expenses and retrieve them even after the
+application is closed.
+
+In the next part of the tutorial, we will continue to enhance the application by using
+[FastApi] and [PyDantic] to create a REST API for the finance manager app, and move the
+database to a separate server.
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
diff --git a/sources/pyside6/doc/tutorials/finance_manager/part3/part3.md b/sources/pyside6/doc/tutorials/finance_manager/part3/part3.md
new file mode 100644 (file)
index 0000000..eb15e2c
--- /dev/null
@@ -0,0 +1,204 @@
+(tutorial_financemanager_partthree)=
+
+# Finance Manager Tutorial - Part 3
+
+In this part of the tutorial, we will extend our finance manager app to create a REST API using
+[FastAPI] and [Uvicorn]. This will allow us to perform server-side operations and interact with the
+SQLite database created in Part 2 using [SQLAlchemy].
+
+[FastAPI] is a modern, fast (high-performance), web framework for building APIs with Python.
+It is built on top of [ASGI (Asynchronous Server Gateway Interface)], which allows for efficient
+handling of concurrent requests.
+
+[Uvicorn] is a lightning-fast ASGI server implementation, using uvloop and httptools. It provides
+a high-performance server for running FastAPI applications.
+
+
+To download the complete source code for this tutorial, visit
+{ref}`example_tutorials_finance_manager_part3`.
+
+## Prerequisites
+
+Before we begin, make sure you have [FastAPI] and [Uvicorn] installed within your
+Python environment.
+
+You can install them using pip:
+
+```bash
+pip install fastapi uvicorn
+```
+
+## Project Structure
+
+The overall project structure in this part of the tutorial is much different from the previous
+parts. We move the frontend part that uses PySide6 and QML to a separate directory called `Frontend`
+and the backend part that uses FastAPI to create a REST API to the SQLite database to a directory
+called `Backend`.
+
+```
+├── Backend
+│   â”œâ”€â”€ database.py
+│   â”œâ”€â”€ main.py
+│   â””── rest_api.py
+├── Frontend
+│   â”œâ”€â”€ Finance
+│   â”‚   â”œâ”€â”€ AddDialog.qml
+│   â”‚   â”œâ”€â”€ FinanceDelegate.qml
+│   â”‚   â”œâ”€â”€ FinancePieChart.qml
+│   â”‚   â”œâ”€â”€ FinanceView.qml
+│   â”‚   â”œâ”€â”€ Main.qml
+│   â”‚   â””── qmldir
+│   â”œâ”€â”€ financemodel.py
+│   â””── main.py
+```
+
+## Let's Get Started!
+
+First, let's create the `Backend` directory and move the `database.py` from the
+[previous part](tutorial_financemanager_parttwo) to the `Backend` directory.
+
+### Backend Setup
+
+### Setting Up FastAPI
+
+Create a new Python file `rest_api.py` in the `Backend` directory and add the following code:
+
+<details>
+<summary class="prominent-summary">rest_api.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Backend/rest_api.py
+---
+language: python
+caption: rest_api.py
+linenos: true
+---
+```
+</details>
+
+In `rest_api.py`, we set up a FastAPI application to handle Create and Read operations for the
+finance data. The file includes:
+
+1. **FastAPI Application**: Initializes a FastAPI instance.
+2. **Pydantic Models**: Defines `FinanceCreate` for input validation and creating new records
+and `FinanceRead` for output formatting. `FinanceRead` includes additional configuration for
+ORM models.
+3. **Database Dependency**: Provides a `get_db` function to manage database sessions.
+4. **Create Endpoint**: A POST endpoint `/finances/` to add new finance entries to the database.
+5. **Read Endpoint**: A GET endpoint `/finances/` to retrieve finance entries from the database.
+
+This setup allows the application to interact with the database, enabling the creation and retrieval
+of finance records via RESTful API endpoints.
+
+### Creating a main Python File for the Backend
+
+Create a new Python file `main.py` in the `Backend` directory and add the following code:
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Backend/main.py
+---
+language: python
+caption: main.py
+linenos: true
+---
+```
+</details>
+
+In `main.py`, along with initializing the database we create a [Uvicorn] server instance to run the
+FastAPI application. The server runs on 127.0.0.1 at port 8000 and reloads automatically when code
+changes are detected.
+
+### Frontend Setup
+
+Then we move on to the frontend part of the application, and connect it to the FastAPI backend.
+We will create a new directory `Frontend`. Move the folder `Finance` and the files `financemodel.py`
+and `main.py` from the previous parts to the `Frontend` directory.
+
+Most of the code remains the same as in the previous parts, with minor changes to connect to the
+REST API.
+
+### Updating the FinanceModel Class
+
+The following changes (highlighted) are made to the `financemodel.py` file which removes
+the database interaction code from Python and adds the REST API interaction code.
+
+<details>
+<summary class="prominent-summary">financemodel.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Frontend/financemodel.py
+---
+language: python
+caption: financemodel.py
+linenos: true
+emphasize-lines: 39-61, 104-114
+---
+```
+</details>
+
+Two methods are overridden for the `FinanceModel` class - `fetchMore` and `canFetchMore`. These
+methods are used to fetch more data from the REST API when the model is scrolled to the end. The data
+is fetched in chunks of 10 entries at a time. Additionally, the `append` method is updated to send a
+POST request to the REST API to add a new finance entry.
+
+### Updating the Main Python File for the Frontend
+
+Finally, we update the `main.py` file in the `Frontend` directory to remove the database
+initialization code.
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Frontend/main.py
+---
+language: python
+caption: main.py
+linenos: true
+---
+```
+</details>
+
+The rest of the code and the QML files remains the same as in the previous parts of the tutorial.
+
+### Running the Application
+
+To run the application, first start the backend FastAPI server by executing the `main.py` file in
+the `Backend` directory using Python:
+
+```bash
+python Backend/main.py
+```
+
+This will start the Uvicorn server running the FastAPI application on `http://127.0.0.1:8000`.
+
+After starting the backend server, run the frontend application by executing the `main.py` file in
+the `Frontend` directory using Python:
+
+```bash
+python Frontend/main.py
+```
+
+This will start the PySide6 application that connects to the FastAPI backend to display the finance
+data.
+
+## Deployment
+
+### Deploying the Application
+
+To deploy the application, follow the same steps as in the
+[first part](tutorial_financemanager_partone) of the tutorial.
+
+## Summary
+
+At the end of this tutorial, you have extended the finance manager app to include a REST API using
+FastAPI and Uvicorn. The backend application interacts with the SQLite database using SQLAlchemy
+and provides endpoints to create and retrieve finance records. The frontend application connects to
+the backend using the REST API to display the finance data.
+
+If you want to extend the application further, you can add more features like updating and deleting
+finance records, adding user authentication etc.
+
+[FastAPI]: https://fastapi.tiangolo.com/
+[Uvicorn]: https://www.uvicorn.org/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
+[SQAlchemy]: https://www.sqlalchemy.org/
index 8a69a3c6fa73fbe9127c274b83e002ff8a2b8825..e9349f0c93d272e6c5f311cae8df34a77b37b1ea 100644 (file)
@@ -1,3 +1,5 @@
+.. _pyside6_tutorials:
+
 Tutorials
 =========
 
@@ -20,70 +22,80 @@ names and how they look like.
 
     .. grid-item-card:: Basic Widget
         :class-item: cover-img
-        :link: basictutorial/widgets.html
+        :link: tutorial_widgets
+        :link-type: ref
         :img-top: basictutorial/widgets.png
 
         Your first QtWidgets Application
 
     .. grid-item-card:: Basic Button
         :class-item: cover-img
-        :link: basictutorial/clickablebutton.html
+        :link: tutorial_clickablebutton
+        :link-type: ref
         :img-top: basictutorial/clickablebutton.png
 
         Using a Simple Button
 
     .. grid-item-card:: Basic Connections
         :class-item: cover-img
-        :link: basictutorial/signals_and_slots.html
+        :link: tutorial_signals_and_slots
+        :link-type: ref
         :img-top: basictutorial/signals_slots.png
 
         Signals and Slots
 
     .. grid-item-card:: Basic Dialog
         :class-item: cover-img
-        :link: basictutorial/dialog.html
+        :link: tutorial_dialog
+        :link-type: ref
         :img-top: basictutorial/dialog.png
 
         Creating a Dialog Application
 
     .. grid-item-card:: Basic Table
         :class-item: cover-img
-        :link: basictutorial/tablewidget.html
+        :link: tutorial_tablewidget
+        :link-type: ref
         :img-top: basictutorial/tablewidget.png
 
         Displaying Data Using a Table Widget
 
     .. grid-item-card:: Basic Tree
         :class-item: cover-img
-        :link: basictutorial/treewidget.html
+        :link: tutorial_treewidget
+        :link-type: ref
         :img-top: basictutorial/treewidget.png
 
         Displaying Data Using a Tree Widget
 
     .. grid-item-card:: Basic ``ui`` files
         :class-item: cover-img
-        :link: basictutorial/uifiles.html
+        :link: tutorial_uifiles
+        :link-type: ref
         :img-top: basictutorial/uifiles.png
 
         Using .ui files from Designer or QtCreator with QUiLoader and pyside6-uic
 
     .. grid-item-card:: Basic ``qrc`` files
         :class-item: cover-img
-        :link: basictutorial/qrcfiles.html
+        :link: tutorial_qrcfiles
+        :link-type: ref
         :img-top: basictutorial/player-new.png
 
         Using .qrc Files (pyside6-rcc)
 
     .. grid-item-card:: Basic Translations
         :class-item: cover-img
-        :link: basictutorial/translations.html
+        :link: tutorial_translations
+        :link-type: ref
         :img-top: basictutorial/translations.png
 
         Translating Applications
 
     .. grid-item-card:: Basic Widget Style
         :class-item: cover-img
-        :link: basictutorial/widgetstyling.html
+        :link: tutorial_widgetstyling
+        :link-type: ref
         :img-top: basictutorial/widgetstyling-yes.png
 
         Styling the Widgets Application
@@ -111,39 +123,53 @@ Quick/QML: Basic tutorials
 
     .. grid-item-card:: Basic Quick
         :class-item: cover-img
-        :link: basictutorial/qml.html
+        :link: tutorial_qml
+        :link-type: ref
         :img-top: basictutorial/greenapplication.png
 
         Your First QtQuick/QML Application
 
     .. grid-item-card:: Basic QML Integration
         :class-item: cover-img
-        :link: qmlintegration/qmlintegration.html
+        :link: tutorial_qmlintegration
+        :link-type: ref
         :img-top: qmlintegration/textproperties_material.png
 
         Python-QML integration
 
     .. grid-item-card:: QML Application
         :class-item: cover-img
-        :link: qmlapp/qmlapplication.html
+        :link: tutorial_qmlapplication
+        :link-type: ref
         :img-top: qmlapp/qmlapplication.png
 
         QML Application Tutorial (QtCreator)
 
     .. grid-item-card:: Advanced QML Integration
         :class-item: cover-img
-        :link: qmlsqlintegration/qmlsqlintegration.html
+        :link: tutorial_qmlsqlintegration
+        :link-type: ref
         :img-top: qmlsqlintegration/example_list_view.png
 
         QML, SQL and PySide Integration Tutorial
 
     .. grid-item-card:: Extended Explorer
         :class-item: cover-img
-        :link: extendedexplorer/extendedexplorer.html
+        :link: tutorial_extendedexplorer
+        :link-type: ref
         :img-top: extendedexplorer/resources/extendedexplorer.webp
 
         Extending an Qt Quick Controls example
 
+    .. grid-item-card:: Finance Manager Tutorial
+        :class-item: cover-img
+        :link: tutorial_financemanager
+        :link-type: ref
+        :img-top: finance_manager/part1/resources/finance_manager.webp
+
+        Finance Manager Tutorial using QtQuick, SQLAlchemy, and FastAPI demonstrating how
+        PySide6 can be used to interact with other popular packages in the Python ecosystem.
+
 .. toctree::
     :maxdepth: 1
     :hidden:
@@ -153,6 +179,7 @@ Quick/QML: Basic tutorials
     qmlapp/qmlapplication.rst
     qmlsqlintegration/qmlsqlintegration.rst
     extendedexplorer/extendedexplorer.md
+    finance_manager/index.md
 
 General Applications
 --------------------
@@ -162,23 +189,34 @@ General Applications
 
     .. grid-item-card:: Data Visualization
         :class-item: cover-img
-        :link: datavisualize/index.html
+        :link: datavisualize_index
+        :link-type: ref
         :img-top: datavisualize/images/datavisualization_app.png
 
         Data Visualization Tool
 
     .. grid-item-card:: Expenses Application
         :class-item: cover-img
-        :link: expenses/expenses.html
+        :link: tutorial_expenses
+        :link-type: ref
         :img-top: expenses/expenses_tool.png
 
         Expenses administration tool
 
+    .. grid-item-card:: Deploying PySide6 Applications to Boot to Qt
+        :class-item: cover-img
+        :link: tutorial_boottoqt_raspberrypi
+        :link-type: ref
+        :img-top: embedded/boot2qt_launcher.png
+
+        Deploying PySide6 Applications to Boot to Qt OS on Raspberry Pi
+
 .. toctree::
     :hidden:
 
     datavisualize/index.rst
     expenses/expenses.rst
+    embedded/boot2qt.md
 
 Qt Overviews
 ------------
@@ -186,7 +224,7 @@ Qt Overviews
 .. toctree::
     :maxdepth: 1
 
-    ../overviews/overviews-main.rst
+    ../overviews/qtdoc-overviews-main.rst
 
 C++ and Python
 --------------
@@ -203,4 +241,3 @@ Debug a PySide6 Application
 
     debugging/mixed_debugging.rst
     debugging/qml_debugging.rst
-
index 3a37cc0f3b849987786dab2ec45048a750a653b6..15e9c81dca0aa95757768455903d8f4f24707e76 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 1bacfd829eb01140efb76010d0c18ce7fe79dbe4..41b168cef0f85c55c531eb41896f9d37c7435f2a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 2c8493aa98f6106454b77c9d03092de5b7824db2..6818c1aa34bf552da8a2ae028e145c6e4d70f983 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index da7d201a8218e52780d074d5d3d0c5955140694b..46b5c312a7a181c91fa0d4868d0feffdb7d759dc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtSql import QSqlDatabase, QSqlError, QSqlQuery
 from datetime import date
index c1293bcbdc931de63058a66db7e390f1fbfea4ea..3e85628722e9db5366dbcbd9c359dc86f5a1b9df 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index 2c2b80157650ef41708795e21fb8cdcbf7082a62..5e926bacaf10f39e3aca0f9182d5c8b7592c131f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import copy
 import os
index da7d201a8218e52780d074d5d3d0c5955140694b..46b5c312a7a181c91fa0d4868d0feffdb7d759dc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtSql import QSqlDatabase, QSqlError, QSqlQuery
 from datetime import date
index 3cc55fa462b74ac862c19be84aac8274cbd2ff81..735d8e021016741217af221a62e9707fe282bea0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 
index b3187e0544a8457409cf422ab500cef618ee4171..cfe52bba844042e581b7eb9fc901011d3087b7ed 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import copy
 import os
index 145d6b73ef8647c2dc3e0cd0cc37ce2ea2e70619..6aba9dd938f76a68b046ca320a4a43ae3f8cf132 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import copy, os
 from PySide6.QtSql import QSqlRelationalDelegate
index bb033c6d25a67e2c68191233b9b8a72150f826b6..e115c321dae8bd6942fe3293504123bf1ff8e900 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtGui import QAction
 from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
index da7d201a8218e52780d074d5d3d0c5955140694b..46b5c312a7a181c91fa0d4868d0feffdb7d759dc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtSql import QSqlDatabase, QSqlError, QSqlQuery
 from datetime import date
index 164fc589a8382cfbef51b669754fc71e8acfd4ef..7d1aea78c491437ec43d5a8e2a36a2ae322ca4d6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import QApplication
index 9a6575dc2453a2fea61d06f6e0a4794488668ffa..9af868772538bec5b974f157010777225ad55c0d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 from PySide6.QtWidgets import QApplication
index d67906fa72b0b84a9328a4a54ebcdb7b8af3cf73..3b234bdf8df740990f3de3180becb9d8d7875677 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import random
index 0b89c2ff86f9f3bb0521aeb41fe5a014fff3ef19..da8924183b00eac938a7166b72ef57d3a3d9ec48 100644 (file)
@@ -8,7 +8,7 @@ to Python to understand this.
 
 Before you start, ensure that all the prerequisites for
 Qt for Python are met. See
-:doc:`Getting Started <../../gettingstarted/index>` for more
+:doc:`Getting Started <../../gettingstarted>` for more
 information. In addition, familiarize yourself with the
 basic differences between Qt in C++ and in Python.
 
@@ -66,23 +66,23 @@ In this example, ``func()`` would treat ``var`` as a local
 name without the ``global`` statement. This would lead to
 a ``NameError`` in the ``value is None`` handling, on
 accessing ``var``. For more information about this, see
-`Python refernce documentation <python refdoc>`_.
+`Python reference documentation <python-refdoc_>`_.
 
-.. _python refdoc: https://docs.python.org/3/reference/simple_stmts.html#the-global-statement
+.. _python-refdoc: https://docs.python.org/3/reference/simple_stmts.html#the-global-statement
 
 .. tip:: Python being an interpreted language, most often
-   the easiest way is to try your idea in the interperter.
+   the easiest way is to try your idea in the interpreter.
    You could call the ``help()`` function in the
    interpreter on any built-in function or keyword in
    Python. For example, a call to ``help(import)`` should
-   provide documentation about the ``import`` statment
+   provide documentation about the ``import`` statement
 
 Last but not the least, try out a few examples to
 familiarize yourself with the Python coding style and
 follow the guidelines outlined in the
-`PEP8 - Style Guide <pep8>`_.
+`PEP8 - Style Guide <pep8_>`_.
 
-.. _pep8: <https://www.python.org/dev/peps/pep-0008/#naming-conventions>
+.. _pep8: https://www.python.org/dev/peps/pep-0008/#naming-conventions
 
 .. code-block:: python
 
diff --git a/sources/pyside6/doc/tutorials/qmlapp/App/Main.qml b/sources/pyside6/doc/tutorials/qmlapp/App/Main.qml
new file mode 100644 (file)
index 0000000..7f9b1d7
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+Page {
+    width: 640
+    height: 480
+    required property var myModel
+
+    header: Label {
+        color: "#15af15"
+        text: qsTr("Where do people use Qt?")
+        font.pointSize: 17
+        font.bold: true
+        font.family: "Arial"
+        renderType: Text.NativeRendering
+        horizontalAlignment: Text.AlignHCenter
+        padding: 10
+    }
+    Rectangle {
+        id: root
+        width: parent.width
+        height: parent.height
+
+        Image {
+            id: image
+            fillMode: Image.PreserveAspectFit
+            anchors.centerIn: root
+            source: "./logo.png"
+            opacity: 0.5
+
+        }
+
+        ListView {
+            id: view
+            anchors.fill: root
+            anchors.margins: 25
+            model: myModel
+            delegate: Text {
+                anchors.leftMargin: 50
+                font.pointSize: 15
+                horizontalAlignment: Text.AlignHCenter
+                text: display
+            }
+        }
+    }
+    NumberAnimation {
+        id: anim
+        running: true
+        target: view
+        property: "contentY"
+        duration: 500
+    }
+}
diff --git a/sources/pyside6/doc/tutorials/qmlapp/App/logo.png b/sources/pyside6/doc/tutorials/qmlapp/App/logo.png
new file mode 100644 (file)
index 0000000..30c621c
Binary files /dev/null and b/sources/pyside6/doc/tutorials/qmlapp/App/logo.png differ
diff --git a/sources/pyside6/doc/tutorials/qmlapp/App/qmldir b/sources/pyside6/doc/tutorials/qmlapp/App/qmldir
new file mode 100644 (file)
index 0000000..f093374
--- /dev/null
@@ -0,0 +1,2 @@
+module App
+Main 254.0 Main.qml
diff --git a/sources/pyside6/doc/tutorials/qmlapp/logo.png b/sources/pyside6/doc/tutorials/qmlapp/logo.png
deleted file mode 100644 (file)
index 30c621c..0000000
Binary files a/sources/pyside6/doc/tutorials/qmlapp/logo.png and /dev/null differ
index 8b1b254402263ececbe2fccb57227132b65db4f8..254d75f1e41915a20f42d13356789e8b740563cd 100644 (file)
@@ -1,46 +1,47 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import urllib.request
 import json
-from pathlib import Path
 
 from PySide6.QtQuick import QQuickView
-from PySide6.QtCore import QStringListModel, QUrl
+from PySide6.QtCore import QStringListModel
 from PySide6.QtGui import QGuiApplication
 
 
 if __name__ == '__main__':
 
-    #get our data
+    # get our data
     url = "http://country.io/names.json"
     response = urllib.request.urlopen(url)
     data = json.loads(response.read().decode('utf-8'))
 
-    #Format and sort the data
+    # Format and sort the data
     data_list = list(data.values())
     data_list.sort()
 
-    #Set up the application window
+    # Set up the application window
     app = QGuiApplication(sys.argv)
     view = QQuickView()
     view.setResizeMode(QQuickView.SizeRootObjectToView)
 
-    #Expose the list to the Qml code
+    # Expose the list to the Qml code
     my_model = QStringListModel()
     my_model.setStringList(data_list)
     view.setInitialProperties({"myModel": my_model})
 
-    #Load the QML file
-    qml_file = Path(__file__).parent / "view.qml"
-    view.setSource(QUrl.fromLocalFile(qml_file.resolve()))
+    # Load the QML file
+    # Add the current directory to the import paths and load the main module.
+    view.engine().addImportPath(sys.path[0])
+    view.loadFromModule("App", "Main")
 
-    #Show the window
+    # Show the window
     if view.status() == QQuickView.Error:
         sys.exit(-1)
     view.show()
 
-    #execute and cleanup
+    # execute and cleanup
     app.exec()
     del view
index c6d72e74202e34b3049de03ff8d8972601d4fa02..9dd3e4347a5c009c1862944e99bd77c6c2a93182 100644 (file)
@@ -1,3 +1,5 @@
+.. _tutorial_qmlapplication:
+
 #########################
 QML Application Tutorial
 #########################
@@ -42,8 +44,9 @@ development process using *Qt Creator*:
    This should create a ``main.py`` and ```main.pyproject`` files
    for the project.
 
-#. Download :download:`view.qml<view.qml>` and :download:`logo.png <logo.png>`
-   and move them to your project folder.
+#. Download :download:`Main.qml<App/Main.qml>`, :download:`qmldir<App/qmldir>`
+   and :download:`logo.png <App/logo.png>` and place them in a subdirectory
+   named `App` in your project folder. This creates a basic QML module.
 
 #. Double-click on ``main.pyproject`` to open it in edit mode, and append
    ``view.qml`` and ``logo.png`` to the **files** list. This is how your
@@ -52,7 +55,7 @@ development process using *Qt Creator*:
    .. code::
 
     {
-        "files": ["main.py", "view.qml", "logo.png"]
+        "files": ["main.py", "App/Main.qml", "App/logo.png", "App/qmldir"]
     }
 
 #. Now that you have the necessary bits for the application, import the
@@ -61,8 +64,8 @@ development process using *Qt Creator*:
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 3-23
-      :emphasize-lines: 7-9,14-17
+      :lines: 5-23
+      :emphasize-lines: 5-7,12-15
 
 #. Now, set up the application window using
    :ref:`PySide6.QtGui.QGuiApplication<qguiapplication>`, which manages the application-wide
@@ -70,8 +73,8 @@ development process using *Qt Creator*:
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 3-28
-      :emphasize-lines: 23-25
+      :lines: 5-28
+      :emphasize-lines: 21-24
 
    .. note:: Setting the resize policy is important if you want the
       root item to resize itself to fit the window or vice-a-versa.
@@ -83,23 +86,23 @@ development process using *Qt Creator*:
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 3-33
-      :emphasize-lines: 28-31
+      :lines: 5-33
+      :emphasize-lines: 26-29
 
-#. Load the ``view.qml`` to the ``QQuickView`` and call ``show()`` to
+#. Load the ``Main.qml`` to the ``QQuickView`` and call ``show()`` to
    display the application window.
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 3-42
-      :emphasize-lines: 33-40
+      :lines: 5-43
+      :emphasize-lines: 31-39
 
 #. Finally, execute the application to start the event loop and clean up.
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 3-
-      :emphasize-lines: 42-44
+      :lines: 5-
+      :emphasize-lines: 41-43
 
 #. Your application is ready to be run now. Select **Projects** mode to
    choose the Python version to run it.
@@ -128,5 +131,5 @@ this application:
 Related information
 ********************
 
-* `QML Reference <https://doc.qt.io/qt-5/qmlreference.html>`_
+* `QML Reference <https://doc.qt.io/qt-6/qmlreference.html>`_
 * :doc:`../qmlintegration/qmlintegration`
diff --git a/sources/pyside6/doc/tutorials/qmlapp/view.qml b/sources/pyside6/doc/tutorials/qmlapp/view.qml
deleted file mode 100644 (file)
index 7f9b1d7..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-
-Page {
-    width: 640
-    height: 480
-    required property var myModel
-
-    header: Label {
-        color: "#15af15"
-        text: qsTr("Where do people use Qt?")
-        font.pointSize: 17
-        font.bold: true
-        font.family: "Arial"
-        renderType: Text.NativeRendering
-        horizontalAlignment: Text.AlignHCenter
-        padding: 10
-    }
-    Rectangle {
-        id: root
-        width: parent.width
-        height: parent.height
-
-        Image {
-            id: image
-            fillMode: Image.PreserveAspectFit
-            anchors.centerIn: root
-            source: "./logo.png"
-            opacity: 0.5
-
-        }
-
-        ListView {
-            id: view
-            anchors.fill: root
-            anchors.margins: 25
-            model: myModel
-            delegate: Text {
-                anchors.leftMargin: 50
-                font.pointSize: 15
-                horizontalAlignment: Text.AlignHCenter
-                text: display
-            }
-        }
-    }
-    NumberAnimation {
-        id: anim
-        running: true
-        target: view
-        property: "contentY"
-        duration: 500
-    }
-}
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/QmlIntegration/Main.qml b/sources/pyside6/doc/tutorials/qmlintegration/QmlIntegration/Main.qml
new file mode 100644 (file)
index 0000000..635603f
--- /dev/null
@@ -0,0 +1,160 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.11
+import QtQuick.Controls 2.1
+import QtQuick.Window 2.1
+import QtQuick.Controls.Material 2.1
+
+import io.qt.textproperties 1.0
+
+ApplicationWindow {
+    id: page
+    width: 800
+    height: 400
+    visible: true
+    Material.theme: Material.Dark
+    Material.accent: Material.Red
+
+    Bridge {
+        id: bridge
+    }
+
+    GridLayout {
+        id: grid
+        columns: 2
+        rows: 3
+
+        ColumnLayout {
+            spacing: 2
+            Layout.columnSpan: 1
+            Layout.preferredWidth: 400
+
+            Text {
+                id: leftlabel
+                Layout.alignment: Qt.AlignHCenter
+                color: "white"
+                font.pointSize: 16
+                text: "Qt for Python"
+                Layout.preferredHeight: 100
+                Material.accent: Material.Green
+            }
+
+            RadioButton {
+                id: italic
+                Layout.alignment: Qt.AlignLeft
+                text: "Italic"
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(italic.text)
+                    leftlabel.font.bold = bridge.getBold(italic.text)
+                    leftlabel.font.underline = bridge.getUnderline(italic.text)
+
+                }
+            }
+            RadioButton {
+                id: bold
+                Layout.alignment: Qt.AlignLeft
+                text: "Bold"
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(bold.text)
+                    leftlabel.font.bold = bridge.getBold(bold.text)
+                    leftlabel.font.underline = bridge.getUnderline(bold.text)
+                }
+            }
+            RadioButton {
+                id: underline
+                Layout.alignment: Qt.AlignLeft
+                text: "Underline"
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(underline.text)
+                    leftlabel.font.bold = bridge.getBold(underline.text)
+                    leftlabel.font.underline = bridge.getUnderline(underline.text)
+                }
+            }
+            RadioButton {
+                id: noneradio
+                Layout.alignment: Qt.AlignLeft
+                text: "None"
+                checked: true
+                onToggled: {
+                    leftlabel.font.italic = bridge.getItalic(noneradio.text)
+                    leftlabel.font.bold = bridge.getBold(noneradio.text)
+                    leftlabel.font.underline = bridge.getUnderline(noneradio.text)
+                }
+            }
+        }
+
+        ColumnLayout {
+            id: rightcolumn
+            spacing: 2
+            Layout.columnSpan: 1
+            Layout.preferredWidth: 400
+            Layout.preferredHeight: 400
+            Layout.fillWidth: true
+
+            RowLayout {
+                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+
+
+                Button {
+                    id: red
+                    text: "Red"
+                    highlighted: true
+                    Material.accent: Material.Red
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(red.text)
+                    }
+                }
+                Button {
+                    id: green
+                    text: "Green"
+                    highlighted: true
+                    Material.accent: Material.Green
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(green.text)
+                    }
+                }
+                Button {
+                    id: blue
+                    text: "Blue"
+                    highlighted: true
+                    Material.accent: Material.Blue
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(blue.text)
+                    }
+                }
+                Button {
+                    id: nonebutton
+                    text: "None"
+                    highlighted: true
+                    Material.accent: Material.BlueGrey
+                    onClicked: {
+                        leftlabel.color = bridge.getColor(nonebutton.text)
+                    }
+                }
+            }
+            RowLayout {
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+                Text {
+                    id: rightlabel
+                    color: "white"
+                    Layout.alignment: Qt.AlignLeft
+                    text: "Font size"
+                    Material.accent: Material.White
+                }
+                Slider {
+                    width: rightcolumn.width*0.6
+                    Layout.alignment: Qt.AlignRight
+                    id: slider
+                    value: 0.5
+                    onValueChanged: {
+                        leftlabel.font.pointSize = bridge.getSize(value)
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/QmlIntegration/qmldir b/sources/pyside6/doc/tutorials/qmlintegration/QmlIntegration/qmldir
new file mode 100644 (file)
index 0000000..3dbea62
--- /dev/null
@@ -0,0 +1,2 @@
+module QmlIntegration
+Main 254.0 Main.qml
index 0a751d7d1b98ecaa08d0846f77b221b0440731dd..aa8706f9359aa982b204298375942b7bcfd67d59 100644 (file)
@@ -1,15 +1,15 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
-from pathlib import Path
 
 from PySide6.QtCore import QObject, Slot
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlApplicationEngine, QmlElement
 from PySide6.QtQuickControls2 import QQuickStyle
 
-import style_rc
+import rc_style  # noqa F401
 
 # To be used on the @QmlElement decorator
 # (QML_IMPORT_MINOR_VERSION is optional)
@@ -24,54 +24,41 @@ class Bridge(QObject):
     def getColor(self, s):
         if s.lower() == "red":
             return "#ef9a9a"
-        elif s.lower() == "green":
+        if s.lower() == "green":
             return "#a5d6a7"
-        elif s.lower() == "blue":
+        if s.lower() == "blue":
             return "#90caf9"
-        else:
-            return "white"
+        return "white"
 
     @Slot(float, result=int)
     def getSize(self, s):
         size = int(s * 34)
-        if size <= 0:
-            return 1
-        else:
-            return size
+        return max(1, size)
 
     @Slot(str, result=bool)
     def getItalic(self, s):
-        if s.lower() == "italic":
-            return True
-        else:
-            return False
+        return s.lower() == "italic"
 
     @Slot(str, result=bool)
     def getBold(self, s):
-        if s.lower() == "bold":
-            return True
-        else:
-            return False
+        return s.lower() == "bold"
 
     @Slot(str, result=bool)
     def getUnderline(self, s):
-        if s.lower() == "underline":
-            return True
-        else:
-            return False
+        return s.lower() == "underline"
 
 
 if __name__ == '__main__':
     app = QGuiApplication(sys.argv)
     QQuickStyle.setStyle("Material")
     engine = QQmlApplicationEngine()
-
-    # Get the path of the current directory, and then add the name
-    # of the QML file, to load it.
-    qml_file = Path(__file__).parent / 'view.qml'
-    engine.load(qml_file)
+    # Add the current directory to the import paths and load the main module.
+    engine.addImportPath(sys.path[0])
+    engine.loadFromModule("QmlIntegration", "Main")
 
     if not engine.rootObjects():
         sys.exit(-1)
 
-    sys.exit(app.exec())
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index ff6fe3e31f3c9b73fcca26bd3f4495a40c184cc1..1caf18d457086e01632310cc91cf2952f34631ed 100644 (file)
@@ -1,3 +1,5 @@
+.. _tutorial_qmlintegration:
+
 Python-QML integration
 ======================
 
@@ -29,8 +31,8 @@ application and PySide6 integration:
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 63-76
-      :emphasize-lines: 4,9
+      :lines: 51-64
+      :emphasize-lines: 6,7
 
    Notice that we only need a :code:`QQmlApplicationEngine` to
    :code:`load` the QML file.
@@ -40,7 +42,7 @@ application and PySide6 integration:
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 14-54
+      :lines: 14-49
       :emphasize-lines: 3,4,7
 
    Notice that the registration happens thanks to the :code:`QmlElement`
@@ -61,7 +63,7 @@ application and PySide6 integration:
    This :code:`id` will help you to get a reference to the element
    that was registered from Python.
 
-   .. literalinclude:: view.qml
+   .. literalinclude:: QmlIntegration/Main.qml
       :linenos:
       :lines: 45-55
       :emphasize-lines: 6-8
@@ -75,13 +77,20 @@ application and PySide6 integration:
    will return *False*, that is how we make sure only one is being
    applied to the text.
 
+#. Put the file into into a directory named :code:`QmlIntegration` along
+   with a file named :code:`qmldir` to describe a basic QML module:
+
+   .. code-block:: text
+
+       module QmlIntegration
+       Main 254.0 Main.qml
+
 #. Each slot verifies if the selected option contains the text associated
    to the property:
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 42-47
-      :emphasize-lines: 4,6
+      :lines: 42-44
 
    Returning *True* or *False* allows you to activate and deactivate
    the properties of the QML UI elements.
@@ -91,7 +100,7 @@ application and PySide6 integration:
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 34-39
+      :lines: 33-36
 
 #. Now, for changing the look of our application, you have two options:
 
@@ -109,13 +118,13 @@ application and PySide6 integration:
       .. literalinclude:: style.qrc
          :linenos:
 
-      Generate the *rc* file running, ``pyside6-rcc style.qrc -o style_rc.py``
+      Generate the *rc* file running, ``pyside6-rcc style.qrc -o rc_style.py``
       And finally import it from your ``main.py`` script.
 
    .. literalinclude:: main.py
       :linenos:
-      :lines: 4-12
-      :emphasize-lines: 9
+      :lines: 5-12
+      :emphasize-lines: 8
 
    You can read more about this configuration file
    `here <https://doc.qt.io/qt-5/qtquickcontrols2-configuration.html>`_.
@@ -124,5 +133,6 @@ application and PySide6 integration:
 
    .. image:: textproperties_material.png
 
-You can :download:`view.qml <view.qml>` and
-:download:`main.py <main.py>` to try this example.
+You can download :download:`Main.qml <QmlIntegration/Main.qml>`,
+:download:`qmldir <QmlIntegration/qmldir>` and :download:`main.py <main.py>`
+to try this example.
diff --git a/sources/pyside6/doc/tutorials/qmlintegration/view.qml b/sources/pyside6/doc/tutorials/qmlintegration/view.qml
deleted file mode 100644 (file)
index 635603f..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-import QtQuick 2.0
-import QtQuick.Layouts 1.11
-import QtQuick.Controls 2.1
-import QtQuick.Window 2.1
-import QtQuick.Controls.Material 2.1
-
-import io.qt.textproperties 1.0
-
-ApplicationWindow {
-    id: page
-    width: 800
-    height: 400
-    visible: true
-    Material.theme: Material.Dark
-    Material.accent: Material.Red
-
-    Bridge {
-        id: bridge
-    }
-
-    GridLayout {
-        id: grid
-        columns: 2
-        rows: 3
-
-        ColumnLayout {
-            spacing: 2
-            Layout.columnSpan: 1
-            Layout.preferredWidth: 400
-
-            Text {
-                id: leftlabel
-                Layout.alignment: Qt.AlignHCenter
-                color: "white"
-                font.pointSize: 16
-                text: "Qt for Python"
-                Layout.preferredHeight: 100
-                Material.accent: Material.Green
-            }
-
-            RadioButton {
-                id: italic
-                Layout.alignment: Qt.AlignLeft
-                text: "Italic"
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(italic.text)
-                    leftlabel.font.bold = bridge.getBold(italic.text)
-                    leftlabel.font.underline = bridge.getUnderline(italic.text)
-
-                }
-            }
-            RadioButton {
-                id: bold
-                Layout.alignment: Qt.AlignLeft
-                text: "Bold"
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(bold.text)
-                    leftlabel.font.bold = bridge.getBold(bold.text)
-                    leftlabel.font.underline = bridge.getUnderline(bold.text)
-                }
-            }
-            RadioButton {
-                id: underline
-                Layout.alignment: Qt.AlignLeft
-                text: "Underline"
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(underline.text)
-                    leftlabel.font.bold = bridge.getBold(underline.text)
-                    leftlabel.font.underline = bridge.getUnderline(underline.text)
-                }
-            }
-            RadioButton {
-                id: noneradio
-                Layout.alignment: Qt.AlignLeft
-                text: "None"
-                checked: true
-                onToggled: {
-                    leftlabel.font.italic = bridge.getItalic(noneradio.text)
-                    leftlabel.font.bold = bridge.getBold(noneradio.text)
-                    leftlabel.font.underline = bridge.getUnderline(noneradio.text)
-                }
-            }
-        }
-
-        ColumnLayout {
-            id: rightcolumn
-            spacing: 2
-            Layout.columnSpan: 1
-            Layout.preferredWidth: 400
-            Layout.preferredHeight: 400
-            Layout.fillWidth: true
-
-            RowLayout {
-                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
-
-
-                Button {
-                    id: red
-                    text: "Red"
-                    highlighted: true
-                    Material.accent: Material.Red
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(red.text)
-                    }
-                }
-                Button {
-                    id: green
-                    text: "Green"
-                    highlighted: true
-                    Material.accent: Material.Green
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(green.text)
-                    }
-                }
-                Button {
-                    id: blue
-                    text: "Blue"
-                    highlighted: true
-                    Material.accent: Material.Blue
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(blue.text)
-                    }
-                }
-                Button {
-                    id: nonebutton
-                    text: "None"
-                    highlighted: true
-                    Material.accent: Material.BlueGrey
-                    onClicked: {
-                        leftlabel.color = bridge.getColor(nonebutton.text)
-                    }
-                }
-            }
-            RowLayout {
-                Layout.fillWidth: true
-                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
-                Text {
-                    id: rightlabel
-                    color: "white"
-                    Layout.alignment: Qt.AlignLeft
-                    text: "Font size"
-                    Material.accent: Material.White
-                }
-                Slider {
-                    width: rightcolumn.width*0.6
-                    Layout.alignment: Qt.AlignRight
-                    id: slider
-                    value: 0.5
-                    onValueChanged: {
-                        leftlabel.font.pointSize = bridge.getSize(value)
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/App/Main.qml b/sources/pyside6/doc/tutorials/qmlsqlintegration/App/Main.qml
new file mode 100644 (file)
index 0000000..889385f
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import ChatModel
+
+ApplicationWindow {
+    id: window
+    title: qsTr("Chat")
+    width: 640
+    height: 960
+    visible: true
+
+    SqlConversationModel {
+        id: chat_model
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        ListView {
+            id: listView
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+            Layout.margins: pane.leftPadding + messageField.leftPadding
+            displayMarginBeginning: 40
+            displayMarginEnd: 40
+            verticalLayoutDirection: ListView.BottomToTop
+            spacing: 12
+            model: chat_model
+            delegate: Column {
+                anchors.right: sentByMe ? listView.contentItem.right : undefined
+                spacing: 6
+
+                readonly property bool sentByMe: model.recipient !== "Me"
+                Row {
+                    id: messageRow
+                    spacing: 6
+                    anchors.right: sentByMe ? parent.right : undefined
+
+                    Rectangle {
+                        width: Math.min(messageText.implicitWidth + 24,
+                            listView.width - (!sentByMe ? messageRow.spacing : 0))
+                        height: messageText.implicitHeight + 24
+                        radius: 15
+                        color: sentByMe ? "lightgrey" : "steelblue"
+
+                        Label {
+                            id: messageText
+                            text: model.message
+                            color: sentByMe ? "black" : "white"
+                            anchors.fill: parent
+                            anchors.margins: 12
+                            wrapMode: Label.Wrap
+                        }
+                    }
+                }
+
+                Label {
+                    id: timestampText
+                    text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm")
+                    color: "lightgrey"
+                    anchors.right: sentByMe ? parent.right : undefined
+                }
+            }
+
+            ScrollBar.vertical: ScrollBar {}
+        }
+
+        Pane {
+            id: pane
+            Layout.fillWidth: true
+
+            RowLayout {
+                width: parent.width
+
+                TextArea {
+                    id: messageField
+                    Layout.fillWidth: true
+                    placeholderText: qsTr("Compose message")
+                    wrapMode: TextArea.Wrap
+                }
+
+                Button {
+                    id: sendButton
+                    text: qsTr("Send")
+                    enabled: messageField.length > 0
+                    onClicked: {
+                        listView.model.send_message("machine", messageField.text, "Me");
+                        messageField.text = "";
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/App/qmldir b/sources/pyside6/doc/tutorials/qmlsqlintegration/App/qmldir
new file mode 100644 (file)
index 0000000..f093374
--- /dev/null
@@ -0,0 +1,2 @@
+module App
+Main 254.0 Main.qml
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/Main/qmldir b/sources/pyside6/doc/tutorials/qmlsqlintegration/Main/qmldir
new file mode 100644 (file)
index 0000000..8ad738e
--- /dev/null
@@ -0,0 +1,2 @@
+module Main
+Main 254.0 Main.qml
diff --git a/sources/pyside6/doc/tutorials/qmlsqlintegration/chat.qml b/sources/pyside6/doc/tutorials/qmlsqlintegration/chat.qml
deleted file mode 100644 (file)
index da58ae9..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import ChatModel
-
-ApplicationWindow {
-    id: window
-    title: qsTr("Chat")
-    width: 640
-    height: 960
-    visible: true
-
-    SqlConversationModel {
-        id: chat_model
-    }
-
-    ColumnLayout {
-        anchors.fill: window
-
-        ListView {
-            id: listView
-            Layout.fillWidth: true
-            Layout.fillHeight: true
-            Layout.margins: pane.leftPadding + messageField.leftPadding
-            displayMarginBeginning: 40
-            displayMarginEnd: 40
-            verticalLayoutDirection: ListView.BottomToTop
-            spacing: 12
-            model: chat_model
-            delegate: Column {
-                anchors.right: sentByMe ? listView.contentItem.right : undefined
-                spacing: 6
-
-                readonly property bool sentByMe: model.recipient !== "Me"
-                Row {
-                    id: messageRow
-                    spacing: 6
-                    anchors.right: sentByMe ? parent.right : undefined
-
-                    Rectangle {
-                        width: Math.min(messageText.implicitWidth + 24,
-                            listView.width - (!sentByMe ? messageRow.spacing : 0))
-                        height: messageText.implicitHeight + 24
-                        radius: 15
-                        color: sentByMe ? "lightgrey" : "steelblue"
-
-                        Label {
-                            id: messageText
-                            text: model.message
-                            color: sentByMe ? "black" : "white"
-                            anchors.fill: parent
-                            anchors.margins: 12
-                            wrapMode: Label.Wrap
-                        }
-                    }
-                }
-
-                Label {
-                    id: timestampText
-                    text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm")
-                    color: "lightgrey"
-                    anchors.right: sentByMe ? parent.right : undefined
-                }
-            }
-
-            ScrollBar.vertical: ScrollBar {}
-        }
-
-        Pane {
-            id: pane
-            Layout.fillWidth: true
-
-            RowLayout {
-                width: parent.width
-
-                TextArea {
-                    id: messageField
-                    Layout.fillWidth: true
-                    placeholderText: qsTr("Compose message")
-                    wrapMode: TextArea.Wrap
-                }
-
-                Button {
-                    id: sendButton
-                    text: qsTr("Send")
-                    enabled: messageField.length > 0
-                    onClicked: {
-                        listView.model.send_message("machine", messageField.text, "Me");
-                        messageField.text = "";
-                    }
-                }
-            }
-        }
-    }
-}
index 314fd5aa5ca4c1e9c30b1b7ee380dc4f642e1bbc..e514adda8cf3327fda73c5db77e837849c1883f6 100644 (file)
@@ -1,16 +1,17 @@
 # Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import sys
 import logging
 
-from PySide6.QtCore import QDir, QFile, QUrl
+from PySide6.QtCore import QCoreApplication, QDir, QFile, QStandardPaths
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlApplicationEngine
 from PySide6.QtSql import QSqlDatabase
 
 # We import the file just to trigger the QmlElement type registration.
-import sqlDialog
+import sqlDialog  # noqa E703
 
 logging.basicConfig(filename="chat.log", level=logging.DEBUG)
 logger = logging.getLogger("logger")
@@ -23,9 +24,10 @@ def connectToDatabase():
         if not database.isValid():
             logger.error("Cannot add database")
 
-    write_dir = QDir("")
+    app_data = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation)
+    write_dir = QDir(app_data)
     if not write_dir.mkpath("."):
-        logger.error("Failed to create writable directory")
+        logger.error(f"Failed to create writable directory {app_data}")
 
     # Ensure that we have a writable location on all devices.
     abs_path = write_dir.absolutePath()
@@ -41,12 +43,18 @@ def connectToDatabase():
 
 if __name__ == "__main__":
     app = QGuiApplication()
+    QCoreApplication.setOrganizationName("QtProject")
+    QCoreApplication.setApplicationName("Chat Tutorial")
+
     connectToDatabase()
 
     engine = QQmlApplicationEngine()
-    engine.load(QUrl("chat.qml"))
+    engine.addImportPath(sys.path[0])
+    engine.loadFromModule("App", "Main")
 
     if not engine.rootObjects():
         sys.exit(-1)
 
-    app.exec()
+    exit_code = app.exec()
+    del engine
+    sys.exit(exit_code)
index eee3f807e01e09a2d63c943436988db0bf9a68cc..85db25defff98689d50c83110152dba0f1fa4392 100644 (file)
@@ -1,3 +1,5 @@
+.. _tutorial_qmlsqlintegration:
+
 QML, SQL and PySide Integration Tutorial
 ########################################
 
@@ -16,7 +18,7 @@ The database contains a single line to mock the beginning of a conversation.
 
 .. literalinclude:: sqlDialog.py
    :linenos:
-   :lines: 4-43
+   :lines: 5-44
 
 The ``SqlConversationModel`` class offers the read-only data model required for the non-editable
 contacts list. It derives from the :ref:`QSqlQueryModel` class, which is the logical choice for
@@ -28,14 +30,7 @@ of a chat application.
 
 .. literalinclude:: sqlDialog.py
    :linenos:
-   :lines: 47-59
-
-In ``setRecipient()``, you set a filter over the returned results from the database, and
-emit a signal every time the recipient of the message changes.
-
-.. literalinclude:: sqlDialog.py
-   :linenos:
-   :lines: 61-70
+   :lines: 48-60
 
 The ``data()`` function falls back to ``QSqlTableModel``'s implementation if the role is not a
 custom user role.
@@ -44,18 +39,16 @@ that field, and then use that index to find the value to be returned.
 
 .. literalinclude:: sqlDialog.py
    :linenos:
-   :lines: 72-79
+   :lines: 62-69
 
 
 In ``roleNames()``, we return a Python dictionary with our custom role and role names as key-values
 pairs, so we can use these roles in QML.
 Alternatively, it can be useful to declare an Enum to hold all of the role values.
-Note that ``names`` has to be a hash to be used as a dictionary key,
-and that's why we're using the ``hash`` function.
 
 .. literalinclude:: sqlDialog.py
    :linenos:
-   :lines: 81-95
+   :lines: 71-78
 
 The ``send_message()`` function uses the given recipient and message to insert a new record into
 the database.
@@ -64,14 +57,14 @@ since all the changes will be cached in the model until you do so.
 
 .. literalinclude:: sqlDialog.py
    :linenos:
-   :lines: 97-116
+   :lines: 80-99
 
-chat.qml
+Main.qml
 --------
 
-Let's look at the ``chat.qml`` file.
+Let's look at the ``Main.qml`` file.
 
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
    :linenos:
    :lines: 4-6
 
@@ -84,9 +77,9 @@ Next, import the Qt Quick Controls module.
 Among other things, this provides access to ``ApplicationWindow``, which replaces the existing
 root type, Window:
 
-Let's step through the ``chat.qml`` file.
+Let's step through the ``App/Main.qml`` file.
 
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
    :linenos:
    :lines: 9-14
 
@@ -101,7 +94,7 @@ Once we've set these, we have a properly sized, empty window ready to be filled
 Because we are exposing the :code:`SqlConversationModel` class to QML, we will
 declare a component to access it:
 
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
    :linenos:
    :lines: 16-18
 
@@ -113,11 +106,11 @@ There are two ways of laying out items in QML: `Item Positioners`_ and `Qt Quick
   resizable user interfaces.
   Below, we use `ColumnLayout`_ to vertically lay out a `ListView`_ and a `Pane`_.
 
-  .. literalinclude:: chat.qml
+  .. literalinclude:: App/Main.qml
      :linenos:
      :lines: 20-23
 
-  .. literalinclude:: chat.qml
+  .. literalinclude:: App/Main.qml
      :linenos:
      :lines: 72-74
 
@@ -148,7 +141,7 @@ remaining space that is left after accommodating the Pane.
 
 Let's look at the ``Listview`` in detail:
 
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
    :linenos:
    :lines: 23-70
 
@@ -177,7 +170,7 @@ At the bottom of the screen, we place a `TextArea`_ item to allow multi-line tex
 button to send the message.
 We use Pane to cover the area under these two items:
 
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
    :linenos:
    :lines: 72-96
 
@@ -194,6 +187,10 @@ recipient and one possible sender for this conversation we're just using strings
 .. _displayMarginEnd: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginEnd-prop
 .. _TextArea: https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html
 
+``Main.qml`` needs to be put into a directory named :code:`App` along
+with a file named ``qmldir`` to describe a basic QML module:
+
+.. literalinclude:: App/qmldir
 
 main.py
 -------
@@ -203,14 +200,14 @@ messages levels that our application will generate (errors, warnings, and inform
 
 .. literalinclude:: main.py
    :linenos:
-   :lines: 4-16
+   :lines: 5-17
 
 ``connectToDatabase()`` creates a connection with the SQLite database, creating the actual file
 if it doesn't already exist.
 
 .. literalinclude:: main.py
    :linenos:
-   :lines: 19-39
+   :lines: 20-41
 
 A few interesting things happen in the ``main`` function:
 
@@ -227,6 +224,6 @@ Finally, the Qt application runs, and your program starts.
 
 .. literalinclude:: main.py
    :linenos:
-   :lines: 42-52
+   :lines: 45-59
 
 .. image:: example_list_view.png
index d728aee59f64b675b5aa0103e3a1d87572d808e1..88e60e70f62281c209793e361766e26e04f1cd6d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 import datetime
 import logging
@@ -58,17 +59,6 @@ class SqlConversationModel(QSqlTableModel):
         self.select()
         logging.debug("Table was loaded successfully.")
 
-    def setRecipient(self, recipient):
-        if recipient == self.recipient:
-            pass
-
-        self.recipient = recipient
-
-        filter_str = (f"(recipient = '{self.recipient}' AND author = 'Me') OR "
-                      f"(recipient = 'Me' AND author='{self.recipient}')")
-        self.setFilter(filter_str)
-        self.select()
-
     def data(self, index, role):
         if role < Qt.UserRole:
             return QSqlTableModel.data(self, index, role)
@@ -81,18 +71,11 @@ class SqlConversationModel(QSqlTableModel):
     def roleNames(self):
         """Converts dict to hash because that's the result expected
         by QSqlTableModel"""
-        names = {}
-        author = "author".encode()
-        recipient = "recipient".encode()
-        timestamp = "timestamp".encode()
-        message = "message".encode()
-
-        names[hash(Qt.UserRole)] = author
-        names[hash(Qt.UserRole + 1)] = recipient
-        names[hash(Qt.UserRole + 2)] = timestamp
-        names[hash(Qt.UserRole + 3)] = message
-
-        return names
+
+        return {int(Qt.UserRole): b"author",
+                Qt.UserRole + 1: b"recipient",
+                Qt.UserRole + 2: b"timestamp",
+                Qt.UserRole + 3: b"message"}
 
     # This is a workaround because PySide doesn't provide Q_INVOKABLE
     # So we declare this as a Slot to be able to call it  from QML
index ebfe123dda323b4a95af7a0f2a57911438735e42..1ad649a4598cfe6b3f38acea088f3f3677fabadc 100644 (file)
@@ -10,8 +10,8 @@ set(CMAKE_AUTOMOC ON)
 set(libpyside_HEADERS # installed below
     class_property.h
     dynamicqmetaobject.h
+    dynamicslot_p.h
     feature_select.h
-    globalreceiverv2.h
     pysideclassdecorator_p.h
     pysideclassinfo.h
     pysideclassinfo_p.h
@@ -46,9 +46,9 @@ set(libpyside_HEADERS # installed below
 set(libpyside_SRC
     class_property.cpp
     dynamicqmetaobject.cpp
+    dynamicslot.cpp
     feature_select.cpp
     signalmanager.cpp
-    globalreceiverv2.cpp
     pysideclassdecorator.cpp
     pysideclassinfo.cpp
     pysideqenum.cpp
index 2bed97ef5e2e4c5e13fd46ef039c7807e11d6fb1..9be7dccb0a2b1d0aea6a84ba41b6db03bcf62cf3 100644 (file)
@@ -79,7 +79,7 @@ PyTypeObject *PyClassPropertyType_TypeF()
 // We call `__init__` while pretending to be a PyProperty_Type instance.
 static int PyClassProperty_tp_init(PyObject *self, PyObject *args, PyObject *kwargs)
 {
-    auto hold = Py_TYPE(self);
+    auto *hold = Py_TYPE(self);
     self->ob_type = &PyProperty_Type;
     auto ret = PepExt_Type_GetInitSlot(&PyProperty_Type)(self, args, kwargs);
     self->ob_type = hold;
@@ -126,7 +126,7 @@ static int SbkObjectType_meta_setattro(PyObject *obj, PyObject *name, PyObject *
 {
     // Use `_PepType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
     // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
-    auto type = reinterpret_cast<PyTypeObject *>(obj);
+    auto *type = reinterpret_cast<PyTypeObject *>(obj);
     PySide::Feature::Select(type);
     PyObject *descr = _PepType_Lookup(type, name);
 
@@ -134,7 +134,7 @@ static int SbkObjectType_meta_setattro(PyObject *obj, PyObject *name, PyObject *
     //   1. `Type.class_prop = value`              --> descr_set: `Type.class_prop.__set__(value)`
     //   2. `Type.class_prop = other_class_prop`   --> setattro:  replace existing `class_prop`
     //   3. `Type.regular_attribute = value`       --> setattro:  regular attribute assignment
-    const auto class_prop = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
+    auto *class_prop = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
     const auto call_descr_set = descr && PyObject_IsInstance(descr, class_prop)
                                 && !PyObject_IsInstance(value, class_prop);
     if (call_descr_set) {
@@ -171,7 +171,7 @@ void init(PyObject *module)
         return;
 
     Py_INCREF(PyClassProperty_TypeF());
-    auto classproptype = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
+    auto *classproptype = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
     PyModule_AddObject(module, "PyClassProperty", classproptype);
 }
 
index 048001f81cbbcb6a57b25599066ba27b2f659bd3..95acab869f1d7da402af2693a99d620ac7d13807 100644 (file)
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
 #include "dynamicqmetaobject.h"
+#include "pysidelogging_p.h"
 #include "pysideqobject.h"
 #include "pysidesignal.h"
 #include "pysidesignal_p.h"
@@ -116,7 +117,7 @@ MetaObjectBuilder::MetaObjectBuilder(PyTypeObject *type, const QMetaObject *meta
 
 MetaObjectBuilder::~MetaObjectBuilder()
 {
-    for (auto *metaObject : m_d->m_cachedMetaObjects)
+    for (const auto *metaObject : m_d->m_cachedMetaObjects)
         free(const_cast<QMetaObject*>(metaObject));
     delete m_d->m_builder;
     delete m_d;
@@ -206,7 +207,7 @@ int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature)
         + ensureBuilder()->addSlot(signature).index();
 }
 
-int MetaObjectBuilder::addSlot(const char *signature)
+int MetaObjectBuilder::addSlot(const QByteArray &signature)
 {
     return m_d->addSlot(signature);
 }
@@ -226,7 +227,7 @@ int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature,
     return m_baseObject->methodCount() + methodBuilder.index();
 }
 
-int MetaObjectBuilder::addSlot(const char *signature, const char *type)
+int MetaObjectBuilder::addSlot(const QByteArray &signature, const QByteArray &type)
 {
     return m_d->addSlot(signature, type);
 }
@@ -240,7 +241,7 @@ int MetaObjectBuilderPrivate::addSignal(const QByteArray &signature)
         + ensureBuilder()->addSignal(signature).index();
 }
 
-int MetaObjectBuilder::addSignal(const char *signature)
+int MetaObjectBuilder::addSignal(const QByteArray &signature)
 {
     return m_d->addSignal(signature);
 }
@@ -249,7 +250,7 @@ void MetaObjectBuilderPrivate::removeMethod(QMetaMethod::MethodType mtype,
                                             int index)
 {
     index -= m_baseObject->methodCount();
-    auto builder = ensureBuilder();
+    auto *builder = ensureBuilder();
     Q_ASSERT(index >= 0 && index < builder->methodCount());
     switch (mtype) {
     case QMetaMethod::Constructor:
@@ -277,6 +278,15 @@ int MetaObjectBuilderPrivate::getPropertyNotifyId(PySideProperty *property) cons
     return notifyId;
 }
 
+static QByteArray msgInvalidPropertyType(const QByteArray &className,
+                                         const QByteArray &propertyName,
+                                         const QByteArray &propertyType)
+{
+    return "QMetaObjectBuilder: Failed to add property \""_ba + propertyName
+           + "\" to \""_ba + className + "\": Invalid property type \""
+           + propertyType + "\"."_ba;
+}
+
 QMetaPropertyBuilder
     MetaObjectBuilderPrivate::createProperty(PySideProperty *property,
                                              const QByteArray &propertyName)
@@ -302,8 +312,13 @@ QMetaPropertyBuilder
             }
         }
     }
-    return builder->addProperty(propertyName, property->d->typeName,
-                                propertyNotifyId);
+    const auto metaType = QMetaType::fromName(property->d->typeName);
+    if (!metaType.isValid()) {
+        const auto &msg = msgInvalidPropertyType(m_builder->className(), propertyName,
+                                                 property->d->typeName);
+         PyErr_WarnEx(PyExc_RuntimeWarning, msg.constData(), 0);
+    }
+    return builder->addProperty(propertyName, property->d->typeName, metaType, propertyNotifyId);
 }
 
 int MetaObjectBuilderPrivate::addProperty(const QByteArray &propertyName,
@@ -351,7 +366,7 @@ void MetaObjectBuilder::addInfo(const char *key, const char *value)
 
 void MetaObjectBuilderPrivate::addInfo(const QMap<QByteArray, QByteArray> &info)
 {
-    auto builder = ensureBuilder();
+    auto *builder = ensureBuilder();
     for (auto i = info.constBegin(), end = info.constEnd(); i != end; ++i)
         builder->addClassInfo(i.key(), i.value());
     m_dirty = true;
@@ -371,7 +386,7 @@ void MetaObjectBuilder::addEnumerator(const char *name, bool flag, bool scoped,
 void MetaObjectBuilderPrivate::addEnumerator(const char *name, bool flag, bool scoped,
                                              const MetaObjectBuilder::EnumValues &entries)
 {
-    auto builder = ensureBuilder();
+    auto *builder = ensureBuilder();
     int have_already = builder->indexOfEnumerator(name);
     if (have_already >= 0)
         builder->removeEnumerator(have_already);
@@ -387,7 +402,7 @@ void MetaObjectBuilderPrivate::addEnumerator(const char *name, bool flag, bool s
 void MetaObjectBuilderPrivate::removeProperty(int index)
 {
     index -= m_baseObject->propertyCount();
-    auto builder = ensureBuilder();
+    auto *builder = ensureBuilder();
     Q_ASSERT(index >= 0 && index < builder->propertyCount());
     builder->removeProperty(index);
     m_dirty = true;
@@ -517,7 +532,7 @@ QString MetaObjectBuilder::formatMetaObject(const QMetaObject *metaObject)
     QTextStream str(&result);
     str << "PySide" << QT_VERSION_MAJOR << ".QtCore.QMetaObject(\""
         << metaObject->className() << '"';
-    if (auto *s = metaObject->superClass())
+    if (const auto *s = metaObject->superClass())
         str << " inherits \"" << s->className() << '"';
     str << ":\n";
 
@@ -567,18 +582,18 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
     // This enforces registering of all signals and slots at type parsing time, and not later at
     // signal connection time, thus making sure no method indices change which would break
     // existing connections.
-    const PyObject *mro = type->tp_mro;
-    const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro);
+    PyObject *mro = type->tp_mro;
+    const Py_ssize_t basesCount = PyTuple_Size(mro);
 
     std::vector<PyTypeObject *> basesToCheck;
     // Prepend the actual type that we are parsing.
-    basesToCheck.reserve(1u + basesCount);
+    basesToCheck.reserve(1U + basesCount);
     basesToCheck.push_back(type);
 
-    auto sbkObjTypeF = SbkObject_TypeF();
-    auto baseObjType = reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type);
+    auto *sbkObjTypeF = SbkObject_TypeF();
+    auto *baseObjType = reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type);
     for (Py_ssize_t i = 0; i < basesCount; ++i) {
-        auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i));
+        auto *baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, i));
         if (baseType != sbkObjTypeF && baseType != baseObjType
             && !PySide::isQObjectDerived(baseType, false)) {
             basesToCheck.push_back(baseType);
@@ -655,7 +670,7 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
         AutoDecRef obName(PyObject_GetAttr(obEnumType, PyMagicName::name()));
         // Everything has been checked already in resolveDelayedQEnums.
         // Therefore, we don't need to error-check here again.
-        auto name = String::toCString(obName);
+        const auto *name = String::toCString(obName);
         AutoDecRef members(PyObject_GetAttr(obEnumType, PyMagicName::members()));
         AutoDecRef items(PyMapping_Items(members));
         Py_ssize_t nr_items = PySequence_Length(items);
@@ -666,7 +681,7 @@ void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type)
             AutoDecRef key(PySequence_GetItem(item, 0));
             AutoDecRef member(PySequence_GetItem(item, 1));
             AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
-            auto ckey = String::toCString(key);
+            const auto *ckey = String::toCString(key);
             auto ivalue = PyLong_AsSsize_t(value);
             entries.push_back(std::make_pair(ckey, int(ivalue)));
         }
index dd33f65f77d4c65cfa8218b735eb8c593e7c2b0e..4ea5351c444ff8e1a1dd856a820aa77f3b63b6eb 100644 (file)
@@ -31,9 +31,9 @@ public:
 
     int indexOfMethod(QMetaMethod::MethodType mtype, const QByteArray &signature) const;
     int indexOfProperty(const QByteArray &name) const;
-    int addSlot(const char *signature);
-    int addSlot(const char *signature, const char *type);
-    int addSignal(const char *signature);
+    int addSlot(const QByteArray &signature);
+    int addSlot(const QByteArray &signature, const QByteArray &type);
+    int addSignal(const QByteArray &signature);
     void removeMethod(QMetaMethod::MethodType mtype, int index);
     int addProperty(const char *property, PyObject *data);
     void addInfo(const char *key, const char *value);
diff --git a/sources/pyside6/libpyside/dynamicslot.cpp b/sources/pyside6/libpyside/dynamicslot.cpp
new file mode 100644 (file)
index 0000000..1fbdba1
--- /dev/null
@@ -0,0 +1,418 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "dynamicslot_p.h"
+#include "pysidestaticstrings.h"
+#include "pysideutils.h"
+#include "pysideweakref.h"
+#include "signalmanager.h"
+
+#include <autodecref.h>
+#include <gilstate.h>
+#include <pep384ext.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QtCompare>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QHash>
+#include <QtCore/QPointer>
+
+namespace PySide
+{
+
+static void disconnectReceiver(PyObject *pythonSelf);
+
+DynamicSlot::SlotType DynamicSlot::slotType(PyObject *callback)
+{
+    if (PyMethod_Check(callback) != 0)
+        return SlotType::Method;
+    if (PySide::isCompiledMethod(callback) != 0)
+        return SlotType::CompiledMethod;
+    if (PyCFunction_Check(callback) != 0)
+        return SlotType::C_Function;
+    return SlotType::Callable;
+}
+
+// Simple callable slot.
+class CallbackDynamicSlot : public DynamicSlot
+{
+    Q_DISABLE_COPY_MOVE(CallbackDynamicSlot)
+public:
+    explicit CallbackDynamicSlot(PyObject *callback) noexcept;
+    ~CallbackDynamicSlot() override;
+
+    void call(const QByteArrayList &parameterTypes, const char *returnType,
+              void **cppArgs) override;
+    void formatDebug(QDebug &debug) const override;
+
+private:
+    PyObject *m_callback;
+};
+
+CallbackDynamicSlot::CallbackDynamicSlot(PyObject *callback) noexcept :
+    m_callback(callback)
+{
+    Py_INCREF(m_callback);
+}
+
+CallbackDynamicSlot::~CallbackDynamicSlot()
+{
+    Shiboken::GilState gil;
+    Py_DECREF(m_callback);
+}
+
+void CallbackDynamicSlot::call(const QByteArrayList &parameterTypes, const char *returnType,
+                               void **cppArgs)
+{
+    SignalManager::callPythonMetaMethod(parameterTypes, returnType, cppArgs, m_callback);
+    // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
+    // error so it considered "handled".
+    if (PyErr_Occurred() != nullptr)
+        SignalManager::handleMetaCallError();
+}
+
+void CallbackDynamicSlot::formatDebug(QDebug &debug) const
+{
+    debug << "CallbackDynamicSlot(" << PySide::debugPyObject(m_callback) << ')';
+}
+
+// A method given by "signal.connect(foo.method)" is a temporarily created
+// callable/partial function where self is bound as a first parameter.
+// It can be split into self and the function. Keeping a reference on
+// the callable itself would prevent object deletion. Instead, keep a
+// reference on the function.
+class MethodDynamicSlot : public DynamicSlot
+{
+    Q_DISABLE_COPY_MOVE(MethodDynamicSlot)
+public:
+
+    explicit MethodDynamicSlot(PyObject *function, PyObject *pythonSelf);
+    ~MethodDynamicSlot() override;
+
+    PyObject *pythonSelf() const { return m_pythonSelf; }
+
+    void call(const QByteArrayList &parameterTypes, const char *returnType,
+              void **cppArgs) override;
+    void formatDebug(QDebug &debug) const override;
+
+private:
+    PyObject *m_function;
+    PyObject *m_pythonSelf;
+};
+
+MethodDynamicSlot::MethodDynamicSlot(PyObject *function, PyObject *pythonSelf) :
+    m_function(function),
+    m_pythonSelf(pythonSelf)
+{
+}
+
+MethodDynamicSlot::~MethodDynamicSlot()
+{
+    Shiboken::GilState gil;
+    Py_DECREF(m_function);
+}
+
+void MethodDynamicSlot::call(const QByteArrayList &parameterTypes, const char *returnType,
+                             void **cppArgs)
+{
+    // create a callback based on method data
+    Shiboken::AutoDecRef callable(PepExt_Type_CallDescrGet(m_function,
+                                                           m_pythonSelf, nullptr));
+    SignalManager::callPythonMetaMethod(parameterTypes, returnType,
+                                        cppArgs, callable.object());
+    // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
+    // error so it considered "handled".
+    if (PyErr_Occurred() != nullptr)
+        SignalManager::handleMetaCallError();
+}
+
+void MethodDynamicSlot::formatDebug(QDebug &debug) const
+{
+    debug << "MethodDynamicSlot(self=" << PySide::debugPyObject(m_pythonSelf)
+        << ", function=" << PySide::debugPyObject(m_function) << ')';
+}
+
+// Store a weak reference on pythonSelf.
+class TrackingMethodDynamicSlot : public MethodDynamicSlot
+{
+    Q_DISABLE_COPY_MOVE(TrackingMethodDynamicSlot)
+public:
+    explicit TrackingMethodDynamicSlot(PyObject *function, PyObject *pythonSelf,
+                                       PyObject *weakRef);
+    ~TrackingMethodDynamicSlot() override;
+
+    void releaseWeakRef() { m_weakRef = nullptr; }
+
+private:
+    PyObject *m_weakRef;
+};
+
+TrackingMethodDynamicSlot::TrackingMethodDynamicSlot(PyObject *function, PyObject *pythonSelf,
+                                                     PyObject *weakRef) :
+    MethodDynamicSlot(function, pythonSelf),
+    m_weakRef(weakRef)
+{
+}
+
+TrackingMethodDynamicSlot::~TrackingMethodDynamicSlot()
+{
+    if (m_weakRef != nullptr) {
+        Shiboken::GilState gil;
+        // weakrefs must not be de-refed after the object has been deleted,
+        // else they get negative refcounts.
+        if (PepExt_Weakref_IsAlive(m_weakRef))
+            Py_DECREF(m_weakRef);
+    }
+}
+
+// Delete the connection on receiver deletion by weakref
+class PysideReceiverMethodSlot : public TrackingMethodDynamicSlot
+{
+    Q_DISABLE_COPY_MOVE(PysideReceiverMethodSlot)
+public:
+    explicit PysideReceiverMethodSlot(PyObject *function, PyObject *pythonSelf);
+
+    ~PysideReceiverMethodSlot() override = default;
+};
+
+static void onPysideReceiverSlotDestroyed(void *data)
+{
+    auto *self = reinterpret_cast<PysideReceiverMethodSlot *>(data);
+    // Ensure the weakref is gone in case the connection stored in
+    // Qt's internals outlives Python.
+    self->releaseWeakRef();
+    Py_BEGIN_ALLOW_THREADS
+    disconnectReceiver(self->pythonSelf());
+    Py_END_ALLOW_THREADS
+}
+
+PysideReceiverMethodSlot::PysideReceiverMethodSlot(PyObject *function, PyObject *pythonSelf) :
+    TrackingMethodDynamicSlot(function, pythonSelf,
+                              WeakRef::create(pythonSelf, onPysideReceiverSlotDestroyed, this))
+{
+}
+
+DynamicSlot* DynamicSlot::create(PyObject *callback)
+{
+    Shiboken::GilState gil;
+    switch (slotType(callback)) {
+    case SlotType::Method: {
+        PyObject *function = PyMethod_GET_FUNCTION(callback);
+        Py_INCREF(function);
+        PyObject *pythonSelf = PyMethod_GET_SELF(callback);
+        return new PysideReceiverMethodSlot(function, pythonSelf);
+    }
+    case SlotType::CompiledMethod: {
+        // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes.
+        PyObject *function = PyObject_GetAttr(callback, PySide::PySideName::im_func());
+        Py_DECREF(function);
+        PyObject *pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self());
+        Py_DECREF(pythonSelf);
+        return new PysideReceiverMethodSlot(function, pythonSelf);
+    }
+    case SlotType::C_Function: // Treat C-function as normal callables
+    case SlotType::Callable:
+        break;
+    }
+    return new CallbackDynamicSlot(callback);
+}
+
+QDebug operator<<(QDebug debug, const DynamicSlot *ds)
+{
+    QDebugStateSaver saver(debug);
+    debug.noquote();
+    debug.nospace();
+    if (ds != nullptr)
+        ds->formatDebug(debug);
+    else
+        debug << "DynamicSlot(0";
+    return debug;
+}
+
+// Connection code for signal connections that use a Python callable not
+// targeting the QMetaMethod of a QObject (no index). They require
+// invocation in a class inheriting QtPrivate::QSlotObjectBase (PySideQSlotObject
+// aggregating DynamicSlot), which is passed to:
+// QObjectPrivate::connect(const QObject *, int signal, QtPrivate::QSlotObjectBase *, ...).
+// For each of those connections (identified by ConnectionKey), we maintain a
+// hash of ConnectionKey->QMetaObject::Connection for:
+//
+// - Disconnecting: Retrieve QMetaObject::Connection for the connection parameters
+//
+// - Tracking sender (QObject) life time and clean hash on destroyed()
+//
+// - Tracking receiver (PyObject*) life time via weakref in case of a
+//       connection to a method and proactively disconnect on weakref
+//       notification as not to cause leaks of the instance.
+
+struct ConnectionKey
+{
+    const QObject *sender;
+    int senderIndex;
+    const PyObject *object;
+    const void *method;
+
+    friend constexpr size_t qHash(const ConnectionKey &k, size_t seed = 0) noexcept
+    {
+        return qHashMulti(seed, k.sender, k.senderIndex, k.object, k.method);
+    }
+
+    friend constexpr bool comparesEqual(const ConnectionKey &lhs,
+                                        const ConnectionKey &rhs) noexcept
+    {
+        return lhs.sender == rhs.sender && lhs.senderIndex == rhs.senderIndex
+               && lhs.object == rhs.object && lhs.method == rhs.method;
+    }
+
+    Q_DECLARE_EQUALITY_COMPARABLE(ConnectionKey);
+};
+
+QDebug operator<<(QDebug debug, const ConnectionKey &k)
+{
+    QDebugStateSaver saver(debug);
+    debug.noquote();
+    debug.nospace();
+    debug << "ConnectionKey(" << static_cast<const void *>(k.sender)
+          << '/' << k.sender->metaObject()->className();
+    auto on =  k.sender->objectName();
+    if (!on.isEmpty())
+        debug << "/\"" << on << '"';
+    debug << ", index=" << k.senderIndex << ", target="
+          << PySide::debugPyObject(const_cast<PyObject *>(k.object));
+    if (k.method != nullptr)
+        debug << ", method=" << k.method;
+    debug << ')';
+    return debug;
+}
+
+QDebug operator<<(QDebug debug, const QMetaObject::Connection &c)
+{
+    QDebugStateSaver saver(debug);
+    debug.noquote();
+    debug.nospace();
+    debug << "Connection(";
+    if (c)
+        debug << static_cast<const void *>(&c); // d-ptr;
+    else
+        debug << '0';
+    debug << ')';
+    return debug;
+}
+
+using ConnectionHash = QHash<ConnectionKey, QMetaObject::Connection>;
+
+static ConnectionHash connectionHash;
+
+static ConnectionKey connectionKey(const QObject *sender, int senderIndex,
+                                   PyObject *callback)
+{
+    PyObject *object{};
+    void *method{};
+
+    switch (DynamicSlot::slotType(callback)) {
+    case DynamicSlot::SlotType::Method:
+        // PYSIDE-1422: Avoid hash on self which might be unhashable.
+        object = PyMethod_GET_SELF(callback);
+        method = PyMethod_GET_FUNCTION(callback);
+        break;
+    case DynamicSlot::SlotType::CompiledMethod: {
+        // PYSIDE-1589: Fix for slots in compiled functions
+        Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
+        Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
+        object = self.object();
+        method = func.object();
+        break;
+    }
+    case DynamicSlot::SlotType::Callable:
+        method = callback;
+        break;
+    case DynamicSlot::SlotType::C_Function:
+        object = PyCFunction_GetSelf(callback);
+        method = reinterpret_cast<void *>(PyCFunction_GetFunction(callback));
+        break;
+    }
+
+    return {sender, senderIndex, object, method};
+}
+
+// Listens to QObject::destroyed of senders and removes them from the hash.
+class SenderSignalDeletionTracker : public QObject
+{
+    Q_OBJECT
+public:
+    using QObject::QObject;
+
+public Q_SLOTS:
+    void senderDestroyed(QObject *o);
+};
+
+void SenderSignalDeletionTracker::senderDestroyed(QObject *o)
+{
+    for (auto it = connectionHash.begin(); it != connectionHash.end(); ) {
+        if (it.key().sender == o)
+            it = connectionHash.erase(it);
+        else
+            ++it;
+    }
+}
+
+static QPointer<SenderSignalDeletionTracker> senderSignalDeletionTracker;
+
+static void disconnectReceiver(PyObject *pythonSelf)
+{
+    // A check for reentrancy was added for PYSIDE-88, but has not been
+    // observed yet.
+    for (bool keepGoing = true; keepGoing; ) {
+        keepGoing = false;
+        for (auto it = connectionHash.begin(); it != connectionHash.end(); ) {
+            if (it.key().object == pythonSelf) {
+                const auto oldSize = connectionHash.size();
+                QObject::disconnect(it.value());
+                it = connectionHash.erase(it);
+                // Check for a disconnection causing deletion of further objects
+                // by a re-entrant call.
+                if (connectionHash.size() < oldSize - 1) {
+                    keepGoing = true;
+                    break; // Iterators were invalidated, retry
+                }
+            } else {
+                ++it;
+            }
+        }
+    }
+}
+
+static void clearConnectionHash()
+{
+    connectionHash.clear();
+}
+
+void registerSlotConnection(QObject *source, int signalIndex, PyObject *callback,
+                            const QMetaObject::Connection &connection)
+{
+    connectionHash.insert(connectionKey(source, signalIndex, callback), connection);
+    if (senderSignalDeletionTracker.isNull()) {
+        auto *app = QCoreApplication::instance();
+        senderSignalDeletionTracker = new SenderSignalDeletionTracker(app);
+        Py_AtExit(clearConnectionHash);
+    }
+
+    QObject::connect(source, &QObject::destroyed,
+                     senderSignalDeletionTracker, &SenderSignalDeletionTracker::senderDestroyed,
+                     Qt::UniqueConnection);
+}
+
+bool disconnectSlot(QObject *source, int signalIndex, PyObject *callback)
+{
+    auto it = connectionHash.find(connectionKey(source, signalIndex, callback));
+    const bool ok = it != connectionHash.end();
+    if (ok) {
+        QObject::disconnect(it.value());
+        connectionHash.erase(it);
+    }
+    return ok;
+}
+
+} // namespace PySide
+
+#include "dynamicslot.moc"
diff --git a/sources/pyside6/libpyside/dynamicslot_p.h b/sources/pyside6/libpyside/dynamicslot_p.h
new file mode 100644 (file)
index 0000000..bad05d7
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef DYNAMICSLOT_P_H
+#define DYNAMICSLOT_P_H
+
+#include <sbkpython.h>
+
+#include <QtCore/QtCompare>
+#include <QtCore/QMetaObject>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+namespace PySide
+{
+
+class DynamicSlot
+{
+    Q_DISABLE_COPY_MOVE(DynamicSlot)
+public:
+    enum SlotType
+    {
+        Callable,
+        Method,
+        CompiledMethod,
+        C_Function
+    };
+
+    virtual ~DynamicSlot() = default;
+
+    virtual void call(const QByteArrayList &parameterTypes, const char *returnType,
+                      void **cppArgs) = 0;
+    virtual void formatDebug(QDebug &debug) const = 0;
+
+    static SlotType slotType(PyObject *callback);
+    static DynamicSlot *create(PyObject *callback);
+
+protected:
+    DynamicSlot() noexcept = default;
+};
+
+QDebug operator<<(QDebug debug, const DynamicSlot *ds);
+
+void registerSlotConnection(QObject *source, int signalIndex, PyObject *callback,
+                            const QMetaObject::Connection &connection);
+bool disconnectSlot(QObject *source, int signalIndex, PyObject *callback);
+
+}
+
+#endif // DYNAMICSLOT_P_H
index cfd465267befad82b3032909bdb681ea01a068ba..305095d1f97c2da1fea494efe8f9a20d6f4c4eec 100644 (file)
@@ -192,7 +192,7 @@ static bool addNewDict(PyTypeObject *type, int select_id)
         return false;
     setSelectId(new_dict, select_id);
     // insert the dict into the ring
-    auto next_dict = nextInCircle(dict);
+    auto *next_dict = nextInCircle(dict);
     setNextDict(dict, new_dict);
     setNextDict(new_dict, next_dict);
     PepType_SetDict(type, new_dict);
@@ -347,10 +347,10 @@ static inline void SelectFeatureSet(PyTypeObject *type)
     last_select_id = select_id;
 
     auto *mro = type->tp_mro;
-    Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
+    const Py_ssize_t n = PyTuple_Size(mro);
     // We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
-    for (idx = 0; idx < n - 2; idx++) {
-        auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+    for (Py_ssize_t idx = 0; idx < n - 2; idx++) {
+        auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
         SelectFeatureSetSubtype(sub_type, select_id);
     }
     // PYSIDE-1436: Clear all caches for the type and subtypes.
@@ -444,10 +444,10 @@ static PyObject *methodWithNewName(PyTypeObject *type,
      * Create a method with a lower case name.
      */
     auto *obtype = reinterpret_cast<PyObject *>(type);
-    int len = strlen(new_name);
-    auto name = new char[len + 1];
+    const auto len = strlen(new_name);
+    auto *name = new char[len + 1];
     strcpy(name, new_name);
-    auto new_meth = new PyMethodDef;
+    auto *new_meth = new PyMethodDef;
     new_meth->ml_name = name;
     new_meth->ml_meth = meth->ml_meth;
     new_meth->ml_flags = meth->ml_flags;
@@ -479,7 +479,8 @@ static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, in
     /*
      * Add objects with lower names to `type->tp_dict` from 'prev_dict`.
      */
-    PyObject *key, *value;
+    PyObject *key{};
+    PyObject *value{};
     Py_ssize_t pos = 0;
 
     // We first copy the things over which will not be changed:
@@ -555,7 +556,7 @@ static PyObject *createProperty(PyTypeObject *type, PyObject *getter, PyObject *
     return prop;
 }
 
-static const QByteArrayList parseFields(const char *propStr, bool *stdWrite)
+static QByteArrayList parseFields(const char *propStr, bool *stdWrite)
 {
     /*
      * Break the string into subfields at ':' and add defaults.
@@ -626,11 +627,11 @@ static QByteArrayList GetPropertyStringsMro(PyTypeObject *type)
     auto res = QByteArrayList();
 
     PyObject *mro = type->tp_mro;
-    const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+    const Py_ssize_t n = PyTuple_Size(mro);
     // We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
     for (Py_ssize_t idx = 0; idx < n - 2; idx++) {
-        auto *subType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
-        auto props = SbkObjectType_GetPropertyStrings(subType);
+        auto *subType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
+        auto *props = SbkObjectType_GetPropertyStrings(subType);
         if (props != nullptr)
             for (; *props != nullptr; ++props)
                 res << QByteArray(*props);
@@ -673,7 +674,7 @@ static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, in
         return true;
 
     for (const auto &propStr : std::as_const(props)) {
-        bool isStdWrite;
+        bool isStdWrite{};
         auto fields = parseFields(propStr, &isStdWrite);
         bool haveWrite = fields.size() == 3;
         PyObject *name = make_snake_case(fields[0], lower);
@@ -758,7 +759,7 @@ static PyGetSetDef property_getset[] = {
 static bool patch_property_impl()
 {
     // Turn `__doc__` into a computed attribute without changing writability.
-    auto gsp = property_getset;
+    auto *gsp = property_getset;
     auto *type = &PyProperty_Type;
     AutoDecRef dict(PepType_GetDict(type));
     AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp
deleted file mode 100644 (file)
index 51070b4..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "globalreceiverv2.h"
-#include "pysideweakref.h"
-#include "pysidestaticstrings.h"
-#include "pysideutils.h"
-#include "signalmanager.h"
-
-#include <autodecref.h>
-#include <gilstate.h>
-#include <pep384ext.h>
-
-#include <QtCore/QMetaMethod>
-#include <QtCore/QSet>
-#include <QtCore/QDebug>
-
-#include <cstring>
-
-#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
-
-
-namespace PySide
-{
-
-class DynamicSlotDataV2
-{
-    Q_DISABLE_COPY_MOVE(DynamicSlotDataV2)
-    public:
-        DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent);
-        ~DynamicSlotDataV2();
-
-        int addSlot(const char *signature);
-        int id(const char *signature) const;
-        PyObject *callback();
-        GlobalReceiverKey key() const { return {m_pythonSelf, m_callback}; }
-        void notify();
-
-        static void onCallbackDestroyed(void *data);
-        static GlobalReceiverKey key(PyObject *callback);
-
-        void formatDebug(QDebug &debug) const;
-
-    private:
-        bool m_isMethod;
-        PyObject *m_callback;
-        PyObject *m_pythonSelf = nullptr;
-        PyObject *m_pyClass = nullptr;
-        PyObject *m_weakRef = nullptr;
-        QMap<QByteArray, int> m_signatures;
-        GlobalReceiverV2 *m_parent;
-};
-
-void DynamicSlotDataV2::formatDebug(QDebug &debug) const
-{
-    debug << "method=" << m_isMethod << ", m_callback=" << m_callback;
-    if (m_callback != nullptr)
-        debug << '/' << Py_TYPE(m_callback)->tp_name;
-    debug << ", self=" << m_pythonSelf;
-    if (m_pythonSelf != nullptr)
-        debug << '/' << Py_TYPE(m_pythonSelf)->tp_name;
-    debug << ", m_pyClass=" << m_pyClass;
-    if (m_pyClass != nullptr)
-        debug << '/' << Py_TYPE(m_pyClass)->tp_name;
-    debug << ", signatures=" << m_signatures.keys();
-}
-
-QDebug operator<<(QDebug debug, const DynamicSlotDataV2 *d)
-{
-    QDebugStateSaver saver(debug);
-    debug.noquote();
-    debug.nospace();
-    debug << "DynamicSlotDataV2(";
-    if (d)
-        d->formatDebug(debug);
-    else
-        debug << '0';
-    debug << ')';
-    return debug;
-}
-
-using namespace PySide;
-
-DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent) :
-    m_parent(parent)
-{
-    Shiboken::GilState gil;
-
-    if (PyMethod_Check(callback)) {
-        m_isMethod = true;
-        // To avoid increment instance reference keep the callback information
-        m_callback = PyMethod_GET_FUNCTION(callback);
-        Py_INCREF(m_callback);
-        m_pythonSelf = PyMethod_GET_SELF(callback);
-
-        //monitor class from method lifetime
-        m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this);
-    } else if (PySide::isCompiledMethod(callback)) {
-        // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes.
-        m_isMethod = true;
-
-        m_callback = PyObject_GetAttr(callback, PySide::PySideName::im_func());
-        Py_DECREF(m_callback);
-
-        m_pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self());
-        Py_DECREF(m_pythonSelf);
-
-        //monitor class from method lifetime
-        m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this);
-    } else {
-        m_isMethod = false;
-
-        m_callback = callback;
-        Py_INCREF(m_callback);
-    }
-}
-
-GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback)
-{
-    Shiboken::GilState gil;
-    if (PyMethod_Check(callback)) {
-        // PYSIDE-1422: Avoid hash on self which might be unhashable.
-        return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)};
-    } else if (PySide::isCompiledMethod(callback)) {
-        // PYSIDE-1589: Fix for slots in compiled functions
-        Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
-        Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
-        return {self, func};
-    }
-    return {nullptr, callback};
-}
-
-PyObject *DynamicSlotDataV2::callback()
-{
-    PyObject *callback = m_callback;
-
-    //create a callback based on method data
-    if (m_isMethod)
-        callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr);
-    else
-        Py_INCREF(callback);
-
-    return callback;
-}
-
-int DynamicSlotDataV2::id(const char *signature) const
-{
-    const auto it = m_signatures.constFind(signature);
-    return it != m_signatures.cend() ? it.value() : -1;
-}
-
-int DynamicSlotDataV2::addSlot(const char *signature)
-{
-    int index = id(signature);
-    if (index == -1)
-        index = m_signatures[signature] = m_parent->metaObjectBuilder().addSlot(signature);
-    return index;
-}
-
-void DynamicSlotDataV2::onCallbackDestroyed(void *data)
-{
-    auto self = reinterpret_cast<DynamicSlotDataV2 *>(data);
-    self->m_weakRef = nullptr;
-    Py_BEGIN_ALLOW_THREADS
-    SignalManager::instance().deleteGlobalReceiver(self->m_parent);
-    Py_END_ALLOW_THREADS
-}
-
-DynamicSlotDataV2::~DynamicSlotDataV2()
-{
-    Shiboken::GilState gil;
-
-    Py_XDECREF(m_weakRef);
-    m_weakRef = nullptr;
-
-    Py_DECREF(m_callback);
-}
-
-const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender";
-
-GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) :
-    QObject(nullptr),
-    m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
-    m_receiver(receiver)
-{
-    m_data = new DynamicSlotDataV2(callback, this);
-}
-
-GlobalReceiverV2::~GlobalReceiverV2()
-{
-    m_refs.clear();
-    // Remove itself from map.
-    // Suppress handling of destroyed() for objects whose last reference is contained inside
-    // the callback object that will now be deleted. The reference could be a default argument,
-    // a callback local variable, etc.
-    // The signal has to be suppressed because it would lead to the following situation:
-    // Callback is deleted, hence the last reference is decremented,
-    // leading to the object being deleted, which emits destroyed(), which would try to invoke
-    // the already deleted callback, and also try to delete the object again.
-    DynamicSlotDataV2 *data = m_data;
-    m_data = nullptr;
-    delete data;
-}
-
-int GlobalReceiverV2::addSlot(const char *signature)
-{
-    return m_data->addSlot(signature);
-}
-
-void GlobalReceiverV2::incRef(const QObject *link)
-{
-    Q_ASSERT(link);
-    m_refs.append(link);
-}
-
-void GlobalReceiverV2::decRef(const QObject *link)
-{
-    Q_ASSERT(link);
-    m_refs.removeOne(link);
-}
-
-void GlobalReceiverV2::notify()
-{
-    purgeDeletedSenders();
-}
-
-static bool isNull(const QPointer<const QObject> &p)
-{
-    return p.isNull();
-}
-
-void GlobalReceiverV2::purgeDeletedSenders()
-{
-    m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end());
-}
-
-bool GlobalReceiverV2::isEmpty() const
-{
-    return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull);
-}
-
-GlobalReceiverKey GlobalReceiverV2::key() const
-{
-    return m_data->key();
-}
-
-GlobalReceiverKey GlobalReceiverV2::key(PyObject *callback)
-{
-    return DynamicSlotDataV2::key(callback);
-}
-
-const QMetaObject *GlobalReceiverV2::metaObject() const
-{
-    return const_cast<GlobalReceiverV2 *>(this)->m_metaObject.update();
-}
-
-int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
-{
-    Shiboken::GilState gil;
-    Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
-    Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
-
-    QMetaMethod slot = metaObject()->method(id);
-    Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
-
-    if (!m_data) {
-        const QByteArray message = "PySide6 Warning: Skipping callback call "
-            + slot.methodSignature() + " because the callback object is being destructed.";
-        PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
-        return -1;
-    }
-
-    const bool setSenderDynamicProperty = !m_receiver.isNull();
-    if (setSenderDynamicProperty)
-        m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender()));
-
-    const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr;
-    Shiboken::AutoDecRef callback(m_data->callback());
-    SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit);
-
-    if (setSenderDynamicProperty)
-        m_receiver->setProperty(senderDynamicProperty, QVariant{});
-
-    // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
-    // error so it considered "handled".
-    if (PyErr_Occurred()) {
-        int reclimit = Py_GetRecursionLimit();
-        // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
-        // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
-        // error again. Don't do it when the limit is already insanely high, to avoid overflow.
-        if (reclimit < (1 << 30))
-            Py_SetRecursionLimit(reclimit + 5);
-        PyErr_Print();
-        Py_SetRecursionLimit(reclimit);
-    }
-
-    return -1;
-}
-
-void GlobalReceiverV2::formatDebug(QDebug &debug) const
-{
-    debug << "receiver=" << m_receiver << ", slot=" << m_data;
-    if (isEmpty())
-        debug << ", empty";
-    else
-        debug << ", refs=" << m_refs;
-};
-
-QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g)
-{
-    QDebugStateSaver saver(debug);
-    debug.noquote();
-    debug.nospace();
-    debug << "GlobalReceiverV2(";
-    if (g)
-        g->formatDebug(debug);
-    else
-        debug << '0';
-    debug << ')';
-    return debug;
-}
-
-} // namespace PySide
diff --git a/sources/pyside6/libpyside/globalreceiverv2.h b/sources/pyside6/libpyside/globalreceiverv2.h
deleted file mode 100644 (file)
index 0e3bc56..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef GLOBALRECEIVER_V2_H
-#define GLOBALRECEIVER_V2_H
-
-#include <sbkpython.h>
-
-#include "dynamicqmetaobject.h"
-
-#include <QtCore/QtCompare>
-#include <QtCore/QByteArray>
-#include <QtCore/QHashFunctions>
-#include <QtCore/QObject>
-#include <QtCore/QPointer>
-#include <QtCore/QMap>
-
-#include <memory>
-
-QT_FORWARD_DECLARE_CLASS(QDebug);
-
-namespace PySide
-{
-
-class DynamicSlotDataV2;
-class GlobalReceiverV2;
-
-struct GlobalReceiverKey
-{
-    const PyObject *object;
-    const PyObject *method;
-
-    friend constexpr size_t qHash(GlobalReceiverKey k, size_t seed = 0) noexcept
-    {
-        return qHashMulti(seed, k.object, k.method);
-    }
-    friend constexpr bool comparesEqual(const GlobalReceiverKey &lhs,
-                                        const GlobalReceiverKey &rhs) noexcept
-    {
-        return lhs.object == rhs.object && lhs.method == rhs.method;
-    }
-    Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(GlobalReceiverKey)
-};
-
-/// A class used to link C++ Signals to non C++ slots (Python callbacks) by
-/// providing fake slots for QObject::connect().
-/// It keeps a Python callback and the list of QObject senders. It is stored
-/// in SignalManager by a hash of the Python callback.
-class GlobalReceiverV2 : public QObject
-{
-public:
-    Q_DISABLE_COPY_MOVE(GlobalReceiverV2)
-
-    /// Create a GlobalReceiver object that will call 'callback'
-    /// @param callback A Python callable object (can be a method or not)
-    explicit GlobalReceiverV2(PyObject *callback, QObject *receiver = nullptr);
-
-    ~GlobalReceiverV2() override;
-
-    /// Reimplemented function from QObject
-    int qt_metacall(QMetaObject::Call call, int id, void **args) override;
-    const QMetaObject *metaObject() const override;
-
-    /// Add a extra slot to this object
-    /// @param  signature The signature of the slot to be added
-    /// @return The index of this slot on metaobject
-    int addSlot(const char *signature);
-
-    /// Notify to GlobalReceiver about when a new connection was made
-    void notify();
-
-    /// Used to increment the reference of the GlobalReceiver object
-    /// @param link This is a parameter used to link the ref to
-    ///             some QObject life.
-    void incRef(const QObject *link);
-
-    /// Used to decrement the reference of the GlobalReceiver object.
-    /// @param link This is a parameter used to dismiss the link
-    ///             ref to some QObject.
-    void decRef(const QObject *link);
-
-    /// Returns whether any senders are registered.
-    bool isEmpty() const;
-
-    /// Use to retrieve the unique hash of this GlobalReceiver object
-    /// @return hash key
-    GlobalReceiverKey key() const;
-
-    /// Use to retrieve the unique hash of the PyObject based on GlobalReceiver rules
-    /// @param callback The Python callable object used to calculate the id
-    /// @return hash key
-    static GlobalReceiverKey key(PyObject *callback);
-
-    const MetaObjectBuilder &metaObjectBuilder() const { return m_metaObject; }
-    MetaObjectBuilder &metaObjectBuilder() { return m_metaObject; }
-
-    static const char *senderDynamicProperty;
-
-    void formatDebug(QDebug &debug) const;
-
-private:
-    void purgeDeletedSenders();
-
-    MetaObjectBuilder m_metaObject;
-    DynamicSlotDataV2 *m_data;
-    using QObjectPointer = QPointer<const QObject>;
-    QList<QObjectPointer> m_refs;
-    QPointer<QObject> m_receiver;
-};
-
-QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g);
-
-}
-
-#endif
index 3a06a03e4d7b3fc6eee54e8dbb0c3091d4c06f53..02cff066b22f73e7dd8ca0c79832a4e46774b110 100644 (file)
 #include <optional>
 #include <typeinfo>
 
+#ifdef Q_OS_WIN
+#  include <conio.h>
+#else
+#  include <QtCore/QDeadlineTimer>
+#  include <QtCore/private/qcore_unix_p.h>
+#endif
+
 using namespace Qt::StringLiterals;
 
 static QStack<PySide::CleanupFunction> cleanupFunctionList;
@@ -91,7 +98,7 @@ void init(PyObject *module)
     ClassProperty::init(module);
     MetaFunction::init(module);
     // Init signal manager, so it will register some meta types used by QVariant.
-    SignalManager::instance();
+    SignalManager::init();
     initQApp();
 }
 
@@ -215,13 +222,13 @@ static QByteArrayList _SbkType_LookupProperty(PyTypeObject *type,
     if (origName.isEmpty())
         return QByteArrayList{};
     PyObject *mro = type->tp_mro;
-    auto n = PyTuple_GET_SIZE(mro);
+    auto n = PyTuple_Size(mro);
     auto len = std::strlen(origName);
     for (Py_ssize_t idx = 0; idx < n; idx++) {
-        PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+        auto *base = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
         if (!SbkObjectType_Check(base))
             continue;
-        auto props = SbkObjectType_GetPropertyStrings(base);
+        auto *props = SbkObjectType_GetPropertyStrings(base);
         if (props == nullptr || *props == nullptr)
             continue;
         for (; *props != nullptr; ++props) {
@@ -255,11 +262,12 @@ static bool _setProperty(PyObject *qObj, PyObject *name, PyObject *value, bool *
     using Shiboken::AutoDecRef;
 
     QByteArray propName(Shiboken::String::toCString(name));
-    auto type = Py_TYPE(qObj);
+    auto *type = Py_TYPE(qObj);
     int flags = currentSelectId(type);
     int prop_flag = flags & 0x02;
     auto found = false;
-    QByteArray getterName{}, setterName{};
+    QByteArray getterName;
+    QByteArray setterName;
 
     auto fields = _SbkType_LookupProperty(type, propName, flags);
     if (!fields.isEmpty()) {
@@ -331,7 +339,7 @@ static std::optional<QMetaMethod> findSignal(const QMetaObject *mo,
         if (method.methodType() == QMetaMethod::Signal && method.name() == name)
             return method;
     }
-    auto *base = mo->superClass();
+    const auto *base = mo->superClass();
     return base != nullptr ? findSignal(base, name) : std::nullopt;
 }
 
@@ -339,7 +347,8 @@ bool fillQtProperties(PyObject *qObj, const QMetaObject *metaObj,
                       PyObject *kwds, bool allowErrors)
 {
 
-    PyObject *key, *value;
+    PyObject *key{};
+    PyObject *value{};
     Py_ssize_t pos = 0;
     int flags = currentSelectId(Py_TYPE(qObj));
     int snake_flag = flags & 0x01;
@@ -396,9 +405,9 @@ void runCleanupFunctions()
 
 static void destructionVisitor(SbkObject *pyObj, void *data)
 {
-    auto realData = reinterpret_cast<void **>(data);
-    auto pyQApp = reinterpret_cast<SbkObject *>(realData[0]);
-    auto pyQObjectType = reinterpret_cast<PyTypeObject *>(realData[1]);
+    auto *realData = reinterpret_cast<void **>(data);
+    auto *pyQApp = reinterpret_cast<SbkObject *>(realData[0]);
+    auto *pyQObjectType = reinterpret_cast<PyTypeObject *>(realData[1]);
 
     if (pyObj != pyQApp && PyObject_TypeCheck(pyObj, pyQObjectType)) {
         if (Shiboken::Object::hasOwnership(pyObj) && Shiboken::Object::isValid(pyObj, false)) {
@@ -417,7 +426,6 @@ void destroyQCoreApplication()
     QCoreApplication *app = QCoreApplication::instance();
     if (!app)
         return;
-    SignalManager::instance().clear();
 
     Shiboken::BindingManager &bm = Shiboken::BindingManager::instance();
     SbkObject *pyQApp = bm.retrieveWrapper(app);
@@ -446,7 +454,7 @@ std::size_t getSizeOfQObject(PyTypeObject *type)
 void initDynamicMetaObject(PyTypeObject *type, const QMetaObject *base, std::size_t cppObjSize)
 {
     //create DynamicMetaObject based on python type
-    auto userData = new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
+    auto *userData = new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
     userData->mo.update();
     Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor<TypeUserData>);
 
@@ -469,7 +477,7 @@ TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj)
 
 TypeUserData *retrieveTypeUserData(PyObject *pyObj)
 {
-    auto pyTypeObj = PyType_Check(pyObj)
+    auto *pyTypeObj = PyType_Check(pyObj)
         ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
     return retrieveTypeUserData(pyTypeObj);
 }
@@ -482,7 +490,7 @@ const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj)
 
 const QMetaObject *retrieveMetaObject(PyObject *pyObj)
 {
-    auto pyTypeObj = PyType_Check(pyObj)
+    auto *pyTypeObj = PyType_Check(pyObj)
         ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
     return retrieveMetaObject(pyTypeObj);
 }
@@ -491,20 +499,19 @@ void initQObjectSubType(PyTypeObject *type, PyObject *args, PyObject * /* kwds *
 {
     PyTypeObject *qObjType = Shiboken::Conversions::getPythonTypeObject("QObject*");
 
-    PyObject *bases = PyTuple_GET_ITEM(args, 1);
-    int numBases = PyTuple_GET_SIZE(bases);
+    PyObject *bases = PyTuple_GetItem(args, 1);
 
     TypeUserData *userData = nullptr;
 
-    for (int i = 0; i < numBases; ++i) {
-        auto base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i));
+    for (Py_ssize_t i = 0, numBases = PyTuple_Size(bases); i < numBases; ++i) {
+        auto *base = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, i));
         if (PyType_IsSubtype(base, qObjType)) {
             userData = retrieveTypeUserData(base);
             break;
         }
     }
     if (!userData) {
-        const char *className = Shiboken::String::toCString(PyTuple_GET_ITEM(args, 0));
+        const char *className = Shiboken::String::toCString(PyTuple_GetItem(args, 0));
         qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.",
                  className);
         return;
@@ -516,6 +523,36 @@ void initQObjectSubType(PyTypeObject *type, PyObject *args, PyObject * /* kwds *
     PySide::Feature::Enable(true);
 }
 
+extern "C" {
+static int qAppInputHook()
+{
+    auto *app = QCoreApplication::instance();
+    if (app == nullptr || app->thread() != QThread::currentThread())
+        return 0;
+#ifndef Q_OS_WIN
+    // Check for press on stdin (file descriptor 0)
+    pollfd stdinPfd =  qt_make_pollfd(0, POLLIN);
+    while (qt_safe_poll(&stdinPfd, 1, QDeadlineTimer{1}) == 0)
+        QCoreApplication::processEvents({}, 50000);
+#else
+    while (_kbhit() == 0)
+        QCoreApplication::processEvents({}, 50000);
+#endif
+    return 0;
+}
+} // extern "C"
+
+static void unregisterQAppInputHook()
+{
+    PyOS_InputHook = nullptr;
+}
+
+static void registerQAppInputHook()
+{
+    PyOS_InputHook = qAppInputHook;
+    qAddPostRoutine(unregisterQAppInputHook);
+}
+
 void initQApp()
 {
     /*
@@ -529,8 +566,10 @@ void initQApp()
      * I would appreciate very much if someone could explain or even fix
      * this issue. It exists only when a pre-existing application exists.
      */
-    if (!qApp)
+    if (qApp == nullptr) {
+        registerQAppInputHook();
         Py_DECREF(MakeQAppWrapper(nullptr));
+    }
 
     // PYSIDE-1470: Register a function to destroy an application from shiboken.
     setDestroyQApplication(destroyQCoreApplication);
@@ -555,7 +594,9 @@ PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *n
 
     // Search on metaobject (avoid internal attributes started with '__')
     if (!attr) {
-        PyObject *type, *value, *traceback;
+        PyObject *type{};
+        PyObject *value{};
+        PyObject *traceback{};
         PyErr_Fetch(&type, &value, &traceback);     // This was omitted for a loong time.
 
         int flags = currentSelectId(Py_TYPE(self));
@@ -569,16 +610,14 @@ PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *n
             // Note: before implementing this property handling, the meta function code
             // below created meta functions which was quite wrong.
             auto *subdict = _PepType_Lookup(Py_TYPE(self), PySideMagicName::property_methods());
-            PyObject *propName = PyDict_GetItem(subdict, name);
-            if (propName) {
+            if (PyObject *propName = PyDict_GetItem(subdict, name)) {
                 // We really have a property name and need to fetch the fget or fset function.
                 static PyObject *const _fget = Shiboken::String::createStaticString("fget");
                 static PyObject *const _fset = Shiboken::String::createStaticString("fset");
                 static PyObject *const _fdel = Shiboken::String::createStaticString("fdel");
                 static PyObject *const arr[3] = {_fget, _fset, _fdel};
-                auto prop = _PepType_Lookup(Py_TYPE(self), propName);
-                for (int idx = 0; idx < 3; ++idx) {
-                    auto *trial = arr[idx];
+                auto *prop = _PepType_Lookup(Py_TYPE(self), propName);
+                for (auto *trial : arr) {
                     auto *res = PyObject_GetAttr(prop, trial);
                     if (res) {
                         AutoDecRef elemName(PyObject_GetAttr(res, PySideMagicName::name()));
@@ -612,18 +651,15 @@ PyObject *getHiddenDataFromQObject(QObject *cppSelf, PyObject *self, PyObject *n
                 if (methMatch) {
                     if (method.methodType() == QMetaMethod::Signal) {
                         signalList.append(method);
-                    } else {
-                        PySideMetaFunction *func = MetaFunction::newObject(cppSelf, i);
-                        if (func) {
-                            PyObject *result = reinterpret_cast<PyObject *>(func);
-                            PyObject_SetAttr(self, name, result);
-                            return result;
-                        }
+                    } else if (auto *func = MetaFunction::newObject(cppSelf, i)) {
+                        auto *result = reinterpret_cast<PyObject *>(func);
+                        PyObject_SetAttr(self, name, result);
+                        return result;
                     }
                 }
             }
             if (!signalList.isEmpty()) {
-                PyObject *pySignal = reinterpret_cast<PyObject *>(
+                auto *pySignal = reinterpret_cast<PyObject *>(
                     Signal::newObjectFromMethod(self, signalList));
                 PyObject_SetAttr(self, name, pySignal);
                 return pySignal;
@@ -681,11 +717,14 @@ static void invalidatePtr(any_t *object)
     if (Py_IsInitialized() == 0)
         return;
 
-    Shiboken::GilState state;
-
-    SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(object);
-    if (wrapper != nullptr)
-        Shiboken::BindingManager::instance().releaseWrapper(wrapper);
+    // Check for existence before locking (fix hang when QObjects
+    // are moved to different threads).
+    auto &bindingManager = Shiboken::BindingManager::instance();
+    if (bindingManager.hasWrapper(object)) {
+        Shiboken::GilState state;
+        if (SbkObject *wrapper = bindingManager.retrieveWrapper(object))
+            bindingManager.releaseWrapper(wrapper);
+    }
 }
 
 static const char invalidatePropertyName[] = "_PySideInvalidatePtr";
@@ -701,13 +740,13 @@ static inline bool isInternalObject(const char *name)
 
 static const QMetaObject *metaObjectCandidate(const QObject *o)
 {
-    auto *metaObject = o->metaObject();
+    const auto *metaObject = o->metaObject();
     // Skip QML helper types and Python objects
     if (hasDynamicMetaObject(o)) {
-        if (auto *super = metaObject->superClass())
+        if (const auto *super = metaObject->superClass())
             metaObject = super;
     }
-    for (auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
+    for (const auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
         if (!isInternalObject(candidate->className())) {
             metaObject = candidate;
             break;
@@ -723,7 +762,7 @@ static const char *typeName(const QObject *cppSelf)
 {
     const char *typeName = typeid(*cppSelf).name();
     if (!Shiboken::Conversions::getConverter(typeName)) {
-        auto *metaObject = metaObjectCandidate(cppSelf);
+        const auto *metaObject = metaObjectCandidate(cppSelf);
         for (; metaObject != nullptr; metaObject = metaObject->superClass()) {
             const char *name = metaObject->className();
             if (Shiboken::Conversions::getConverter(name)) {
@@ -751,7 +790,7 @@ PyTypeObject *getTypeForQObject(const QObject *cppSelf)
 
 PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type)
 {
-    PyObject *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
+    auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
     if (pyOut) {
         Py_INCREF(pyOut);
         return pyOut;
@@ -773,7 +812,7 @@ PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type)
         }
     }
 
-    pyOut = Shiboken::Object::newObject(sbk_type, cppSelf, false, false, typeName(cppSelf));
+    pyOut = Shiboken::Object::newObjectWithHeuristics(sbk_type, cppSelf, false, typeName(cppSelf));
 
     return pyOut;
 }
@@ -805,17 +844,17 @@ PyObject *qStringToPyUnicode(QStringView s)
 QString pyStringToQString(PyObject *str)
 {
     if (str == Py_None)
-        return QString();
+        return {};
 
     if (PyUnicode_Check(str) != 0)
         return pyUnicodeToQString(str);
 
     if (PyBytes_Check(str)) {
-        const char *asciiBuffer = PyBytes_AS_STRING(str);
+        const char *asciiBuffer = PyBytes_AsString(str);
         if (asciiBuffer)
             return QString::fromLatin1(asciiBuffer);
     }
-    return QString();
+    return {};
 }
 
 // PySide-1499: Provide an efficient, correct PathLike interface
@@ -824,7 +863,7 @@ QString pyPathToQString(PyObject *path)
     // For empty constructors path can be nullptr
     // fallback to an empty QString in that case.
     if (!path)
-        return QString();
+        return {};
 
     // str or bytes pass through
     if (PyUnicode_Check(path) || PyBytes_Check(path))
@@ -833,7 +872,7 @@ QString pyPathToQString(PyObject *path)
     // Let PyOS_FSPath do its work and then fix the result for Windows.
     Shiboken::AutoDecRef strPath(PyOS_FSPath(path));
     if (strPath.isNull())
-        return QString();
+        return {};
     return QDir::fromNativeSeparators(pyStringToQString(strPath));
 }
 
@@ -1062,8 +1101,8 @@ static void formatPySequence(PyObject *obj, QDebug &debug)
 
 static void formatPyDict(PyObject *obj, QDebug &debug)
 {
-    PyObject *key;
-    PyObject *value;
+    PyObject *key{};
+    PyObject *value{};
     Py_ssize_t pos = 0;
     bool first = true;
     debug << '{';
@@ -1150,7 +1189,13 @@ static void formatPyObject(PyObject *obj, QDebug &debug)
         return;
     }
     if (PyType_Check(obj) == 0)
-        debug << pyTypeName(obj) << ": ";
+        debug << pyTypeName(obj);
+    const auto refs = Py_REFCNT(obj);
+    if (refs == UINT_MAX) // _Py_IMMORTAL_REFCNT
+        debug << ", immortal";
+    else
+        debug << ", refs=" << refs;
+    debug << ": ";
     formatPyObjectValue(obj, debug);
 }
 
index 3943725393d3387802c157c1df7580461ac3d18c..eb45cb9a8810e31bb1b317e06134abaacfc4fc8b 100644 (file)
@@ -12,4 +12,3 @@ Version: @BINDING_API_VERSION_FULL@
 Libs: -L${libdir} -lpyside6@pyside6_SUFFIX@@SHIBOKEN_PYTHON_CONFIG_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@
 Cflags: -I${includedir}
 Requires: shiboken6
-
index ec69c5fe71c3c32948227d2e5d71355ebab378c2..4386dcba66b56536b7caa2547792748bbb6fd8d4 100644 (file)
@@ -38,7 +38,7 @@ PyObject *DecoratorPrivate::tp_call_check(PyObject *args, CheckMode checkMode) c
         return nullptr;
     }
 
-    auto type = reinterpret_cast<PyTypeObject *>(arg);
+    auto *type = reinterpret_cast<PyTypeObject *>(arg);
 
     if (checkMode != CheckMode::None && !Shiboken::ObjectType::checkType(type)) {
         PyErr_Format(PyExc_TypeError,
@@ -59,7 +59,7 @@ int StringDecoratorPrivate::convertToString(PyObject *self, PyObject *args)
 {
     int result = -1;
     if (PyTuple_Size(args) == 1) {
-        PyObject *arg = PyTuple_GET_ITEM(args, 0);
+        PyObject *arg = PyTuple_GetItem(args, 0);
         if (PyUnicode_Check(arg)) {
             auto *pData = DecoratorPrivate::get<StringDecoratorPrivate>(self);
             result = 0;
@@ -90,7 +90,7 @@ int TypeDecoratorPrivate::convertToType(PyObject *self, PyObject *args)
     int result = -1;
     const auto argsCount = PyTuple_Size(args);
     if (argsCount == 1) {
-        PyObject *arg = PyTuple_GET_ITEM(args, 0);
+        PyObject *arg = PyTuple_GetItem(args, 0);
         if (PyType_Check(arg)) {
             result = 0;
             auto *pData = DecoratorPrivate::get<TypeDecoratorPrivate>(self);
index 6068f6a2ec9bef00b97fa3dbb4754c531ef2b771..951cdd7ee71e869749512c2068cccd7c8113b6fa 100644 (file)
@@ -72,7 +72,7 @@ protected:
     /// \param self self
     /// \param args Arguments
     /// \return 0 if the parameter is correct, else -1 (for tp_init())
-    int convertToString(PyObject *self, PyObject *args);
+    static int convertToString(PyObject *self, PyObject *args);
 
 private:
     QByteArray m_string;
@@ -92,7 +92,7 @@ protected:
     /// \param self self
     /// \param args Arguments
     /// \return 0 if the parameter is correct, else -1 (for tp_init())
-    int convertToType(PyObject *self, PyObject *args);
+    static int convertToType(PyObject *self, PyObject *args);
 
 private:
     PyTypeObject *m_type = nullptr;
@@ -127,8 +127,8 @@ struct Methods
 
     static void tp_free(void *self)
     {
-        auto pySelf = reinterpret_cast<PyObject *>(self);
-        auto decorator = reinterpret_cast<PySideClassDecorator *>(self);
+        auto *pySelf = reinterpret_cast<PyObject *>(self);
+        auto *decorator = reinterpret_cast<PySideClassDecorator *>(self);
         delete decorator->d;
         PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
     }
index 698cb1c76da3cb9f516f981ac73edb2756e997fa..db643df92bb45f992d292c5ca8f612597015d39b 100644 (file)
@@ -54,7 +54,7 @@ PyObject *ClassInfoPrivate::tp_call(PyObject *self, PyObject *args, PyObject * /
     if (pData->m_alreadyWrapped)
         return PyErr_Format(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
 
-    PyTypeObject *klassType = reinterpret_cast<PyTypeObject *>(klass);
+    auto *klassType = reinterpret_cast<PyTypeObject *>(klass);
     if (!PySide::ClassInfo::setClassInfo(klassType, pData->m_data))
         return PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
 
@@ -69,7 +69,7 @@ int ClassInfoPrivate::tp_init(PyObject *self, PyObject *args, PyObject *kwds)
     PyObject *infoDict = nullptr;
     auto size = PyTuple_Size(args);
     if (size == 1 && kwds == nullptr) {
-        PyObject *tmp = PyTuple_GET_ITEM(args, 0);
+        PyObject *tmp = PyTuple_GetItem(args, 0);
         if (PyDict_Check(tmp))
             infoDict = tmp;
     } else if (size == 0 && kwds && PyDict_Check(kwds)) {
index e8173b97d4746db536274128a2b570ec3de919b7..540e933ae8080b21bdd6527cb79aaf7f3a0aa458 100644 (file)
@@ -51,15 +51,15 @@ PyTypeObject *PySideMetaFunction_TypeF(void)
 
 void functionFree(void *self)
 {
-    PySideMetaFunction *function = reinterpret_cast<PySideMetaFunction *>(self);
+    auto *function = reinterpret_cast<PySideMetaFunction *>(self);
     delete function->d;
 }
 
 PyObject *functionCall(PyObject *self, PyObject *args, PyObject * /* kw */)
 {
-    PySideMetaFunction *function = reinterpret_cast<PySideMetaFunction *>(self);
+    auto *function = reinterpret_cast<PySideMetaFunction *>(self);
 
-    PyObject *retVal;
+    PyObject *retVal{};
     if (!PySide::MetaFunction::call(function->d->qobject, function->d->methodIndex, args, &retVal))
         return nullptr;
     return retVal;
@@ -107,7 +107,7 @@ bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal)
 
     // args given plus return type
     Shiboken::AutoDecRef sequence(PySequence_Fast(args, nullptr));
-    qsizetype numArgs = PySequence_Fast_GET_SIZE(sequence.object()) + 1;
+    qsizetype numArgs = PySequence_Size(sequence.object()) + 1;
 
     if (numArgs - 1 > argTypes.size()) {
         PyErr_Format(PyExc_TypeError, "%s only accepts %d argument(s), %d given!",
@@ -123,12 +123,12 @@ bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal)
         return false;
     }
 
-    QVariant *methValues = new QVariant[numArgs];
+    auto *methValues = new QVariant[numArgs];
     void **methArgs = new void *[numArgs];
 
     // Prepare room for return type
     const char *returnType = method.typeName();
-    if (returnType && std::strcmp("void", returnType))
+    if (returnType && std::strcmp("void", returnType) != 0)
         argTypes.prepend(returnType);
     else
         argTypes.prepend(QByteArray());
@@ -156,12 +156,13 @@ bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal)
             methArgs[i] = methValues[i].data();
             if (i == 0) // Don't do this for return type
                 continue;
+            Shiboken::AutoDecRef obj(PySequence_GetItem(sequence.object(), i - 1));
             if (metaType.id() == QMetaType::QString) {
                 QString tmp;
-                converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), &tmp);
+                converter.toCpp(obj, &tmp);
                 methValues[i] = tmp;
             } else {
-                converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), methArgs[i]);
+                converter.toCpp(obj, methArgs[i]);
             }
         } else {
             PyErr_Format(PyExc_TypeError, "Unknown type used to call meta function (that may be a signal): %s", argTypes[i].constData());
@@ -194,4 +195,3 @@ bool call(QObject *self, int methodIndex, PyObject *args, PyObject **retVal)
 }
 
 } //namespace PySide::MetaFunction
-
index 3720815dbf9694950d11e8957c0e4b5fffa8881d..7c04000510b16240bc359434d9b048a7a2b5fdb2 100644 (file)
@@ -94,12 +94,12 @@ PyTypeObject *PySideProperty_TypeF(void)
 PySidePropertyPrivate::PySidePropertyPrivate() noexcept = default;
 PySidePropertyPrivate::~PySidePropertyPrivate() = default;
 
-PyObject *PySidePropertyPrivate::getValue(PyObject *source)
+PyObject *PySidePropertyPrivate::getValue(PyObject *source) const
 {
     if (fget) {
         Shiboken::AutoDecRef args(PyTuple_New(1));
         Py_INCREF(source);
-        PyTuple_SET_ITEM(args, 0, source);
+        PyTuple_SetItem(args, 0, source);
         return  PyObject_CallObject(fget, args);
     }
     return nullptr;
@@ -109,8 +109,8 @@ int PySidePropertyPrivate::setValue(PyObject *source, PyObject *value)
 {
     if (fset && value) {
         Shiboken::AutoDecRef args(PyTuple_New(2));
-        PyTuple_SET_ITEM(args, 0, source);
-        PyTuple_SET_ITEM(args, 1, value);
+        PyTuple_SetItem(args, 0, source);
+        PyTuple_SetItem(args, 1, value);
         Py_INCREF(source);
         Py_INCREF(value);
         Shiboken::AutoDecRef result(PyObject_CallObject(fset, args));
@@ -118,7 +118,7 @@ int PySidePropertyPrivate::setValue(PyObject *source, PyObject *value)
     }
     if (fdel) {
         Shiboken::AutoDecRef args(PyTuple_New(1));
-        PyTuple_SET_ITEM(args, 0, source);
+        PyTuple_SetItem(args, 0, source);
         Py_INCREF(source);
         Shiboken::AutoDecRef result(PyObject_CallObject(fdel, args));
         return (result.isNull() ? -1 : 0);
@@ -132,7 +132,7 @@ int PySidePropertyPrivate::reset(PyObject *source)
     if (freset) {
         Shiboken::AutoDecRef args(PyTuple_New(1));
         Py_INCREF(source);
-        PyTuple_SET_ITEM(args, 0, source);
+        PyTuple_SetItem(args, 0, source);
         Shiboken::AutoDecRef result(PyObject_CallObject(freset, args));
         return (result.isNull() ? -1 : 0);
     }
@@ -188,7 +188,7 @@ static PyObject *qpropertyTpNew(PyTypeObject *subtype, PyObject * /* args */, Py
 static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds)
 {
     PyObject *type{};
-    auto data = reinterpret_cast<PySideProperty *>(self);
+    auto *data = reinterpret_cast<PySideProperty *>(self);
     PySidePropertyPrivate *pData = data->d;
 
     static const char *kwlist[] = {"type", "fget", "fset", "freset", "fdel", "doc", "notify",
@@ -228,7 +228,7 @@ static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds)
     Py_XINCREF(pData->pyTypeObject);
     pData->typeName = PySide::Signal::getTypeName(type);
 
-    if (pData->typeName.isEmpty())
+    if (type == Py_None || pData->typeName.isEmpty())
         PyErr_SetString(PyExc_TypeError, "Invalid property type or type name.");
     else if (pData->constant && ((pData->fset && pData->fset != Py_None)
                                  || (pData->notify && pData->notify != Py_None)))
@@ -253,11 +253,9 @@ static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds)
 static void qpropertyDeAlloc(PyObject *self)
 {
     qpropertyClear(self);
-    if (PepRuntime_38_flag) {
-        // PYSIDE-939: Handling references correctly.
-        // This was not needed before Python 3.8 (Python issue 35810)
-        Py_DECREF(Py_TYPE(self));
-    }
+    // PYSIDE-939: Handling references correctly.
+    // This was not needed before Python 3.8 (Python issue 35810)
+    Py_DECREF(Py_TYPE(self));
     PyObject_GC_UnTrack(self);
     PepExt_TypeCallFree(self);
 }
@@ -298,7 +296,7 @@ _property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *reset, PyO
     else
         doc = pData->doc;
 
-    auto notify = pData->notify ? pData->notify : Py_None;
+    auto *notify = pData->notify ? pData->notify : Py_None;
 
     PyObject *obNew = PyObject_CallFunction(type, const_cast<char *>("OOOOOsO" "bbb" "bbb"),
         pData->pyTypeObject, get, set, reset, del, doc.data(), notify,
@@ -338,39 +336,43 @@ static PyObject *qPropertyCall(PyObject *self, PyObject *args, PyObject * /* kw
 
 static PyObject *qProperty_fget(PyObject *self, void *)
 {
-    auto func = reinterpret_cast<PySideProperty *>(self)->d->fget;
-    auto ret = func != nullptr ? func : Py_None;
-    Py_INCREF(ret);
-    return ret;
+    auto *func = reinterpret_cast<PySideProperty *>(self)->d->fget;
+    if (func == nullptr)
+        Py_RETURN_NONE;
+    Py_INCREF(func);
+    return func;
 }
 
 static PyObject *qProperty_fset(PyObject *self, void *)
 {
-    auto func = reinterpret_cast<PySideProperty *>(self)->d->fset;
-    auto ret = func != nullptr ? func : Py_None;
-    Py_INCREF(ret);
-    return ret;
+    auto *func = reinterpret_cast<PySideProperty *>(self)->d->fset;
+    if (func == nullptr)
+        Py_RETURN_NONE;
+    Py_INCREF(func);
+    return func;
 }
 
 static PyObject *qProperty_freset(PyObject *self, void *)
 {
-    auto func = reinterpret_cast<PySideProperty *>(self)->d->freset;
-    auto ret = func != nullptr ? func : Py_None;
-    Py_INCREF(ret);
-    return ret;
+    auto *func = reinterpret_cast<PySideProperty *>(self)->d->freset;
+    if (func == nullptr)
+        Py_RETURN_NONE;
+    Py_INCREF(func);
+    return func;
 }
 
 static PyObject *qProperty_fdel(PyObject *self, void *)
 {
-    auto func = reinterpret_cast<PySideProperty *>(self)->d->fdel;
-    auto ret = func != nullptr ? func : Py_None;
-    Py_INCREF(ret);
-    return ret;
+    auto *func = reinterpret_cast<PySideProperty *>(self)->d->fdel;
+    if (func == nullptr)
+        Py_RETURN_NONE;
+    Py_INCREF(func);
+    return func;
 }
 
 static PyObject *qPropertyDocGet(PyObject *self, void *)
 {
-    auto data = reinterpret_cast<PySideProperty *>(self);
+    auto *data = reinterpret_cast<PySideProperty *>(self);
     PySidePropertyPrivate *pData = data->d;
 
     QByteArray doc(pData->doc);
@@ -389,9 +391,11 @@ static PyObject *qPropertyDocGet(PyObject *self, void *)
              * subclass instance instead, otherwise it gets shadowed by
              * __doc__ in the class's dict.
              */
-            auto get_doc_obj = get_doc.object();
-            int err = PyObject_SetAttr(self, PyMagicName::doc(), get_doc);
-            return err < 0 ? nullptr : (Py_INCREF(get_doc_obj), get_doc_obj);
+            auto *get_doc_obj = get_doc.object();
+            if (PyObject_SetAttr(self, PyMagicName::doc(), get_doc) < 0)
+                return nullptr;
+            Py_INCREF(get_doc_obj);
+            return get_doc_obj;
         }
         PyErr_Clear();
     }
@@ -400,7 +404,7 @@ static PyObject *qPropertyDocGet(PyObject *self, void *)
 
 static int qPropertyDocSet(PyObject *self, PyObject *value, void *)
 {
-    auto data = reinterpret_cast<PySideProperty *>(self);
+    auto *data = reinterpret_cast<PySideProperty *>(self);
     PySidePropertyPrivate *pData = data->d;
 
     if (String::check(value)) {
@@ -452,9 +456,9 @@ static PyObject *getFromType(PyTypeObject *type, PyObject *name)
     auto *attr = PyDict_GetItem(tpDict.object(), name);
     if (!attr) {
         PyObject *bases = type->tp_bases;
-        const Py_ssize_t size = PyTuple_GET_SIZE(bases);
+        const Py_ssize_t size = PyTuple_Size(bases);
         for (Py_ssize_t i = 0; i < size; ++i) {
-            PyObject *base = PyTuple_GET_ITEM(bases, i);
+            PyObject *base = PyTuple_GetItem(bases, i);
             attr = getFromType(reinterpret_cast<PyTypeObject *>(base), name);
             if (attr)
                 return attr;
@@ -466,16 +470,20 @@ static PyObject *getFromType(PyTypeObject *type, PyObject *name)
 namespace PySide::Property {
 
 static const char *Property_SignatureStrings[] = {
-    "PySide6.QtCore.Property(self,type:type,fget:typing.Callable=None,fset:typing.Callable=None,"
-        "freset:typing.Callable=None,fdel:typing.Callable=None,doc:str=None,"
-        "notify:typing.Callable=None,designable:bool=True,scriptable:bool=True,"
+    "PySide6.QtCore.Property(self,type:type,"
+        "fget:typing.Optional[typing.Callable[[typing.Any],typing.Any]],"
+        "fset:typing.Optional[typing.Callable[[typing.Any,typing.Any],None]],"
+        "freset:typing.Optional[typing.Callable[[typing.Any,typing.Any],None]],"
+        "doc:str=None,"
+        "notify:typing.Optional[typing.Callable[[],None]],"
+        "designable:bool=True,scriptable:bool=True,"
         "stored:bool=True,user:bool=False,constant:bool=False,final:bool=False)",
-    "PySide6.QtCore.Property.deleter(self,fdel:typing.Callable)->PySide6.QtCore.Property",
-    "PySide6.QtCore.Property.getter(self,fget:typing.Callable)->PySide6.QtCore.Property",
-    "PySide6.QtCore.Property.read(self,fget:typing.Callable)->PySide6.QtCore.Property",
-    "PySide6.QtCore.Property.setter(self,fset:typing.Callable)->PySide6.QtCore.Property",
-    "PySide6.QtCore.Property.write(self,fset:typing.Callable)->PySide6.QtCore.Property",
-    "PySide6.QtCore.Property.__call__(self, func:typing.Callable)->PySide6.QtCore.Property",
+    "PySide6.QtCore.Property.deleter(self,fdel:typing.Callable[[typing.Any],None])->PySide6.QtCore.Property",
+    "PySide6.QtCore.Property.getter(self,fget:typing.Callable[[typing.Any],typing.Any])->PySide6.QtCore.Property",
+    "PySide6.QtCore.Property.read(self,fget:typing.Callable[[typing.Any],typing.Any])->PySide6.QtCore.Property",
+    "PySide6.QtCore.Property.setter(self,fset:typing.Callable[[typing.Any,typing.Any],None])->PySide6.QtCore.Property",
+    "PySide6.QtCore.Property.write(self,fset:typing.Callable[[typing.Any,typing.Any],None])->PySide6.QtCore.Property",
+    "PySide6.QtCore.Property.__call__(self, func:typing.Callable[...,typing.Any])->PySide6.QtCore.Property",
     nullptr}; // Sentinel
 
 void init(PyObject *module)
index 10cb3ce874bf48024eb0fcaf5d41860457e77a4c..7ff5c12372df7086500e23a6f7c221685f7e0ab2 100644 (file)
@@ -26,7 +26,7 @@ public:
 
     virtual void metaCall(PyObject *source, QMetaObject::Call call, void **args);
 
-    PyObject *getValue(PyObject *source);
+    PyObject *getValue(PyObject *source) const;
     int setValue(PyObject *source, PyObject *value);
     int reset(PyObject *source);
 
index c0479160fddd7e9d7b160773426c942c9f2c49da..897c9f4d0b778cbec335c5e88e2b6c12542db62f 100644 (file)
@@ -75,7 +75,7 @@ static PyObject *analyzePyEnum(PyObject *pyenum)
 
 static Py_ssize_t get_lineno()
 {
-    PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame());  // borrowed ref
+    auto *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame());  // borrowed ref
     AutoDecRef ob_lineno(PyObject_GetAttr(frame, Shiboken::PyName::f_lineno()));
     if (ob_lineno.isNull() || !PyLong_Check(ob_lineno))
         return -1;
@@ -84,7 +84,7 @@ static Py_ssize_t get_lineno()
 
 static bool is_module_code()
 {
-    PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame());  // borrowed ref
+    auto *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame());  // borrowed ref
     AutoDecRef ob_code(PyObject_GetAttr(frame, Shiboken::PyName::f_code()));
     if (ob_code.isNull())
         return false;
@@ -133,14 +133,14 @@ PyObject *QEnumMacro(PyObject *pyenum, bool flag)
         return nullptr;
     if (bool(computedFlag) != flag) {
         AutoDecRef name(PyObject_GetAttr(pyenum, PyMagicName::qualname()));
-        auto cname = String::toCString(name);
+        const auto *cname = String::toCString(name);
         const char *e = "Enum";
         const char *f = "Flag";
         PyErr_Format(PyExc_TypeError, "expected '%s' but got '%s' (%.200s)",
                                       flag ? f : e, flag ? e : f, cname);
         return nullptr;
     }
-    auto ok = analyzePyEnum(pyenum);
+    auto *ok = analyzePyEnum(pyenum);
     if (ok == nullptr)
         return nullptr;
     if (is_module_code()) {
@@ -169,7 +169,7 @@ std::vector<PyObject *> resolveDelayedQEnums(PyTypeObject *containerType)
      */
     if (enumCollector.empty())
         return {};
-    PyObject *obContainerType = reinterpret_cast<PyObject *>(containerType);
+    auto *obContainerType = reinterpret_cast<PyObject *>(containerType);
     Py_ssize_t lineno = get_lineno();
 
     std::vector<PyObject *> result;
index 914be898ad658e26a0b674fd1561da8b2b815c7b..279a3d4e047672e9af484586b38b9e7b255ea0f2 100644 (file)
@@ -2,28 +2,42 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
 #include "pysideqslotobject_p.h"
+#include "dynamicslot_p.h"
 
-#include <autodecref.h>
 #include <gilstate.h>
 
 namespace PySide
 {
 
+PySideQSlotObject::PySideQSlotObject(PyObject *callable,
+                                     const QByteArrayList &parameterTypes,
+                                     const char *returnType) :
+    QtPrivate::QSlotObjectBase(&impl),
+    m_dynamicSlot(DynamicSlot::create(callable)),
+    m_parameterTypes(parameterTypes),
+    m_returnType(returnType)
+{
+}
+
+void PySideQSlotObject::call(void **args)
+{
+    Shiboken::GilState state;
+    m_dynamicSlot->call(m_parameterTypes, m_returnType, args);
+}
+
+PySideQSlotObject::~PySideQSlotObject() = default;
+
 void PySideQSlotObject::impl(int which, QSlotObjectBase *this_, QObject *receiver,
                              void **args, bool *ret)
 {
-    auto self = static_cast<PySideQSlotObject *>(this_);
+    auto *self = static_cast<PySideQSlotObject *>(this_);
     switch (which) {
     case Destroy:
         delete self;
         break;
     case Call:
-        {
-            Shiboken::GilState state;
-            Shiboken::AutoDecRef arglist(PyTuple_New(0));
-            Shiboken::AutoDecRef ret(PyObject_CallObject(self->callable, arglist));
-            break;
-        }
+        self->call(args);
+        break;
     case Compare:
     case NumOperations:
         Q_UNUSED(receiver);
index d7d2585055d7cb3045756824aba41a3db0bb4bd8..875369c27103b0054b8359c00a1af7abe1ec7584 100644 (file)
 #include <QtCore/QObject>
 #include <QtCore/qobjectdefs.h>
 
+#include <memory>
+
 namespace PySide
 {
 
+class DynamicSlot;
+
 class PySideQSlotObject : public QtPrivate::QSlotObjectBase
 {
-    PyObject *callable;
+    Q_DISABLE_COPY_MOVE(PySideQSlotObject)
+public:
+    explicit PySideQSlotObject(PyObject *callable, const QByteArrayList &parameterTypes,
+                               const char *returnType  = nullptr);
+    ~PySideQSlotObject();
 
+private:
     static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **args, bool *ret);
+    void call(void **args);
 
-public:
-    PySideQSlotObject(PyObject *callable) : QtPrivate::QSlotObjectBase(&impl), callable(callable)
-    {
-        Py_INCREF(callable);
-    }
-
-    ~PySideQSlotObject()
-    {
-        auto gstate = PyGILState_Ensure();
-        Py_DECREF(callable);
-        PyGILState_Release(gstate);
-    }
+    std::unique_ptr<DynamicSlot> m_dynamicSlot;
+    const QByteArrayList m_parameterTypes;
+    const char *m_returnType;
 };
 
 
index 11e07cb048db1d4a8c3affdf7dbcc6b1d616a532..1e9cd28f9126bfc91ac34ee951ffdd9f3cfbf935 100644 (file)
@@ -11,6 +11,7 @@
 #include "signalmanager.h"
 
 #include <shiboken.h>
+#include <sbkstaticstrings.h>
 
 #include <QtCore/QByteArray>
 #include <QtCore/QDebug>
 #include <QtCore/QMetaObject>
 #include <pep384ext.h>
 #include <signature.h>
+#include <sbkenum.h>
 
 #include <algorithm>
 #include <optional>
 #include <utility>
 #include <cstring>
 
-#define QT_SIGNAL_SENTINEL '2'
+static constexpr char QT_SIGNAL_SENTINEL = '2';
 
 using namespace Qt::StringLiterals;
 
@@ -70,6 +72,11 @@ QDebug operator<<(QDebug debug, const PySideSignalInstancePrivate &d)
     return debug;
 }
 
+static bool isSourceDeleted(const PySideSignalInstance *p)
+{
+    return p->d == nullptr || p->d->shared->deleted;
+}
+
 static bool connection_Check(PyObject *o)
 {
     if (o == nullptr || o == Py_None)
@@ -105,7 +112,8 @@ static std::optional<QByteArrayList> parseArgumentNames(PyObject *argArguments)
 
 namespace PySide::Signal {
     static QByteArray buildSignature(const QByteArray &, const QByteArray &);
-    static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *, PyObject *, int);
+    static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *,
+                                   const PySideSignalInstanceSharedPtr &shared, int);
     static PySideSignalData::Signature parseSignature(PyObject *);
     static PyObject *buildQtCompatible(const QByteArray &);
 } // PySide::Signal
@@ -163,7 +171,7 @@ static PyTypeObject *createMetaSignalType()
     return SbkType_FromSpec(&PySideMetaSignalType_spec);
 }
 
-static PyTypeObject *PySideMetaSignal_TypeF(void)
+static PyTypeObject *PySideMetaSignal_TypeF()
 {
     static auto *type = createMetaSignalType();
     return type;
@@ -204,7 +212,7 @@ PyTypeObject *PySideSignal_TypeF(void)
 static PyObject *signalInstanceRepr(PyObject *obSelf)
 {
     auto *self = reinterpret_cast<PySideSignalInstance *>(obSelf);
-    auto *typeName = Py_TYPE(obSelf)->tp_name;
+    const auto *typeName = Py_TYPE(obSelf)->tp_name;
     return Shiboken::String::fromFormat("<%s %s at %p>", typeName,
                                         self->d ? self->d->signature.constData()
                                                 : "(no signature)", obSelf);
@@ -261,7 +269,7 @@ static int signalTpInit(PyObject *obSelf, PyObject *args, PyObject *kwds)
         return -1;
 
     bool tupledArgs = false;
-    PySideSignal *self = reinterpret_cast<PySideSignal *>(obSelf);
+    auto *self = reinterpret_cast<PySideSignal *>(obSelf);
     if (!self->data)
         self->data = new PySideSignalData;
     if (argName)
@@ -275,7 +283,7 @@ static int signalTpInit(PyObject *obSelf, PyObject *args, PyObject *kwds)
     self->data->signalArguments = argumentNamesOpt.value();
 
     for (Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) {
-        PyObject *arg = PyTuple_GET_ITEM(args, i);
+        PyObject *arg = PyTuple_GetItem(args, i);
         if (PySequence_Check(arg) && !Shiboken::String::check(arg) && !PyEnumMeta_Check(arg)) {
             tupledArgs = true;
             self->data->signatures.append(PySide::Signal::parseSignature(arg));
@@ -290,8 +298,8 @@ static int signalTpInit(PyObject *obSelf, PyObject *args, PyObject *kwds)
 
 static void signalFree(void *vself)
 {
-    auto pySelf = reinterpret_cast<PyObject *>(vself);
-    auto self = reinterpret_cast<PySideSignal *>(vself);
+    auto *pySelf = reinterpret_cast<PyObject *>(vself);
+    auto *self = reinterpret_cast<PySideSignal *>(vself);
     if (self->data) {
         delete self->data;
         self->data = nullptr;
@@ -304,7 +312,7 @@ static void signalFree(void *vself)
 
 static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
 {
-    auto self = reinterpret_cast<PySideSignal *>(obSelf);
+    auto *self = reinterpret_cast<PySideSignal *>(obSelf);
     QByteArray sigKey;
     if (key) {
         sigKey = PySide::Signal::parseSignature(key).signature;
@@ -318,7 +326,7 @@ static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
 
 static PyObject *signalToString(PyObject *obSelf)
 {
-    auto self = reinterpret_cast<PySideSignal *>(obSelf);
+    auto *self = reinterpret_cast<PySideSignal *>(obSelf);
     QByteArray result;
     if (self->data == nullptr || self->data->signatures.isEmpty()) {
         result = "<invalid>"_ba;
@@ -335,7 +343,7 @@ static PyObject *signalToString(PyObject *obSelf)
 
 static PyObject *signalGetAttr(PyObject *obSelf, PyObject *name)
 {
-    auto self = reinterpret_cast<PySideSignal *>(obSelf);
+    auto *self = reinterpret_cast<PySideSignal *>(obSelf);
 
     if (PyUnicode_CompareWithASCIIString(name, "signatures") != 0)
         return PyObject_GenericGetAttr(obSelf, name);
@@ -354,8 +362,8 @@ static PyObject *signalGetAttr(PyObject *obSelf, PyObject *name)
 
 static void signalInstanceFree(void *vself)
 {
-    auto pySelf = reinterpret_cast<PyObject *>(vself);
-    auto self = reinterpret_cast<PySideSignalInstance *>(vself);
+    auto *pySelf = reinterpret_cast<PyObject *>(vself);
+    auto *self = reinterpret_cast<PySideSignalInstance *>(vself);
 
     PySideSignalInstancePrivate *dataPvt = self->d;
     if (dataPvt) {
@@ -365,10 +373,10 @@ static void signalInstanceFree(void *vself)
             Py_DECREF(dataPvt->next);
             dataPvt->next = nullptr;
         }
+        dataPvt->shared->deleted = true;
         delete dataPvt;
         self->d = nullptr;
     }
-    self->deleted = true;
     PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
 }
 
@@ -473,6 +481,32 @@ static PySideSignalInstance *findSignalInstance(PySideSignalInstance *source, in
     return nullptr;
 }
 
+static PySideSignalInstance *findSignalInstanceForSlot(PySideSignalInstance *source,
+                                                       PyObject *slot)
+{
+    Q_ASSERT(slot != nullptr && slot != Py_None);
+
+    // Check signature of the slot (method or function) to match signal
+    const auto args = extractFunctionArgumentsFromSlot(slot);
+
+    if (args.function != nullptr && source->d->next != nullptr) {
+        auto slotArgRange = argCount(args);
+        if (args.isMethod) {
+            slotArgRange.min -= 1;
+            slotArgRange.max -= 1;
+        }
+
+        // Get signature args
+        // Iterate the possible types of connection for this signal and compare
+        // it with slot arguments
+        for (int slotArgs = slotArgRange.max; slotArgs >= slotArgRange.min; --slotArgs) {
+            if (auto *matchedSlot = findSignalInstance(source, slotArgs))
+                return matchedSlot;
+        }
+    }
+    return source;
+}
+
 static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds)
 {
     PyObject *slot = nullptr;
@@ -483,10 +517,10 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
         "O|O:SignalInstance", const_cast<char **>(kwlist), &slot, &type))
         return nullptr;
 
-    PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
+    auto *source = reinterpret_cast<PySideSignalInstance *>(self);
     if (!source->d)
         return PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance");
-    if (source->deleted)
+    if (isSourceDeleted(source))
         return PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted");
 
     Shiboken::AutoDecRef pyArgs(PyList_New(0));
@@ -497,15 +531,15 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
 
         //find best match
         while (sourceWalk && !match) {
-            auto targetWalk = reinterpret_cast<PySideSignalInstance *>(slot);
+            auto *targetWalk = reinterpret_cast<PySideSignalInstance *>(slot);
             while (targetWalk && !match) {
                 if (QMetaObject::checkConnectArgs(sourceWalk->d->signature,
                                                   targetWalk->d->signature)) {
-                    PyList_Append(pyArgs, sourceWalk->d->source);
+                    PyList_Append(pyArgs, sourceWalk->d->shared->source);
                     Shiboken::AutoDecRef sourceSignature(PySide::Signal::buildQtCompatible(sourceWalk->d->signature));
                     PyList_Append(pyArgs, sourceSignature);
 
-                    PyList_Append(pyArgs, targetWalk->d->source);
+                    PyList_Append(pyArgs, targetWalk->d->shared->source);
                     Shiboken::AutoDecRef targetSignature(PySide::Signal::buildQtCompatible(targetWalk->d->signature));
                     PyList_Append(pyArgs, targetSignature);
 
@@ -516,39 +550,13 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
             sourceWalk = reinterpret_cast<PySideSignalInstance *>(sourceWalk->d->next);
         }
     } else {
-        // Check signature of the slot (method or function) to match signal
-        const auto args = extractFunctionArgumentsFromSlot(slot);
-        PySideSignalInstance *matchedSlot = nullptr;
-
-        if (args.function != nullptr) {
-            auto slotArgRange = argCount(args);
-            if (args.isMethod) {
-                slotArgRange.min -= 1;
-                slotArgRange.max -= 1;
-            }
-
-            // Get signature args
-            // Iterate the possible types of connection for this signal and compare
-            // it with slot arguments
-            for (int slotArgs = slotArgRange.max;
-                 slotArgs >= slotArgRange.min && matchedSlot == nullptr; --slotArgs) {
-                 matchedSlot = findSignalInstance(source, slotArgs);
-            }
-        }
-
         // Adding references to pyArgs
-        PyList_Append(pyArgs, source->d->source);
-
-        if (matchedSlot != nullptr) {
-            // If a slot matching the same number of arguments was found,
-            // include signature to the pyArgs
-            Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
-            PyList_Append(pyArgs, signature);
-        } else {
-            // Try the first by default if the slot was not found
-            Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
-            PyList_Append(pyArgs, signature);
-        }
+        PyList_Append(pyArgs, source->d->shared->source);
+
+        // Check signature of the slot (method or function) to match signal
+        PySideSignalInstance *matchedSlot = findSignalInstanceForSlot(source, slot);
+        Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
+        PyList_Append(pyArgs, signature);
         PyList_Append(pyArgs, slot);
         match = true;
     }
@@ -558,7 +566,7 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
 
     if (match) {
         Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
-        Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+        Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->shared->source,
                                                        PySide::PySideName::qtConnect()));
         if (pyMethod.isNull())  // PYSIDE-79: check if pyMethod exists.
             return PyErr_Format(PyExc_RuntimeError, "method 'connect' vanished!");
@@ -575,22 +583,22 @@ static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject
 
 static int argCountInSignature(const char *signature)
 {
-    return QByteArray(signature).count(",") + 1;
+    return QByteArrayView{signature}.count(',') + 1;
 }
 
 static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
 {
-    PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
+    auto *source = reinterpret_cast<PySideSignalInstance *>(self);
     if (!source->d)
         return PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance");
 
     // PYSIDE-2201: Check if the object has vanished meanwhile.
     //              Tried to revive it without exception, but this gives problems.
-    if (source->deleted)
+    if (isSourceDeleted(source))
         return PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
 
     Shiboken::AutoDecRef pyArgs(PyList_New(0));
-    int numArgsGiven = PySequence_Fast_GET_SIZE(args);
+    Py_ssize_t numArgsGiven = PySequence_Size(args);
     int numArgsInSignature = argCountInSignature(source->d->signature);
 
     // If number of arguments given to emit is smaller than the first source signature expects,
@@ -617,7 +625,7 @@ static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
     for (Py_ssize_t i = 0, max = PyTuple_Size(args); i < max; i++)
         PyList_Append(pyArgs, PyTuple_GetItem(args, i));
 
-    Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+    Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->shared->source,
                                                    PySide::PySideName::qtEmit()));
 
     Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
@@ -632,7 +640,7 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
     const auto sig = PySide::Signal::buildSignature(sigName, sigKey);
     for (auto *data = firstSignal; data != nullptr; data = data->d->next) {
         if (data->d->signature == sig) {
-            PyObject *result = reinterpret_cast<PyObject *>(data);
+            auto *result = reinterpret_cast<PyObject *>(data);
             Py_INCREF(result);
             return result;
         }
@@ -653,8 +661,13 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
 static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature)
 {
     if (PyErr_Occurred() != nullptr) { // avoid "%S" invoking str() when an error is set.
+        PyObject *exc{};
+        PyObject *inst{};
+        PyObject *tb{};
+        PyErr_Fetch(&exc, &inst, &tb);
         PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%s) from signal \"%s\".",
                          Py_TYPE(aSlot)->tp_name, signature.constData());
+        PyErr_Restore(exc, inst, tb);
     } else {
         PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%S) from signal \"%s\".",
                          aSlot, signature.constData());
@@ -663,25 +676,25 @@ static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signa
 
 static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
 {
-    auto source = reinterpret_cast<PySideSignalInstance *>(self);
+    auto *source = reinterpret_cast<PySideSignalInstance *>(self);
     if (!source->d)
         return PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance");
 
     Shiboken::AutoDecRef pyArgs(PyList_New(0));
 
     PyObject *slot = Py_None;
-    if (PyTuple_Check(args) && PyTuple_GET_SIZE(args))
-        slot = PyTuple_GET_ITEM(args, 0);
+    if (PyTuple_Check(args) && PyTuple_Size(args))
+        slot = PyTuple_GetItem(args, 0);
 
     bool match = false;
     if (Py_TYPE(slot) == PySideSignalInstance_TypeF()) {
-        PySideSignalInstance *target = reinterpret_cast<PySideSignalInstance *>(slot);
+        auto *target = reinterpret_cast<PySideSignalInstance *>(slot);
         if (QMetaObject::checkConnectArgs(source->d->signature, target->d->signature)) {
-            PyList_Append(pyArgs, source->d->source);
+            PyList_Append(pyArgs, source->d->shared->source);
             Shiboken::AutoDecRef source_signature(PySide::Signal::buildQtCompatible(source->d->signature));
             PyList_Append(pyArgs, source_signature);
 
-            PyList_Append(pyArgs, target->d->source);
+            PyList_Append(pyArgs, target->d->shared->source);
             Shiboken::AutoDecRef target_signature(PySide::Signal::buildQtCompatible(target->d->signature));
             PyList_Append(pyArgs, target_signature);
             match = true;
@@ -690,9 +703,10 @@ static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
         PyList_Append(pyArgs, slot);
         match = true;
     } else {
-        //try the first signature
-        PyList_Append(pyArgs, source->d->source);
-        Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
+        // try the matching signature, fall back to first
+        auto *matchedSlot = slot != Py_None ? findSignalInstanceForSlot(source, slot) : source;
+        PyList_Append(pyArgs, matchedSlot->d->shared->source);
+        Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
         PyList_Append(pyArgs, signature);
 
         // disconnect all, so we need to use the c++ signature disconnect(qobj, signal, 0, 0)
@@ -704,7 +718,7 @@ static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
 
     if (match) {
         Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
-        Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+        Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->shared->source,
                                                        PySide::PySideName::qtDisconnect()));
         PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
         if (result != Py_True)
@@ -719,7 +733,7 @@ static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
 // PYSIDE-68: Supply the missing __get__ function
 static PyObject *signalDescrGet(PyObject *self, PyObject *obj, PyObject * /*type*/)
 {
-    auto signal = reinterpret_cast<PySideSignal *>(self);
+    auto *signal = reinterpret_cast<PySideSignal *>(self);
     // Return the unbound signal if there is nothing to bind it to.
     if (obj == nullptr || obj == Py_None
         || !PySide::isQObjectDerived(Py_TYPE(obj), true)) {
@@ -742,7 +756,7 @@ static PyObject *signalDescrGet(PyObject *self, PyObject *obj, PyObject * /*type
 
 static PyObject *signalCall(PyObject *self, PyObject *args, PyObject *kw)
 {
-    auto signal = reinterpret_cast<PySideSignal *>(self);
+    auto *signal = reinterpret_cast<PySideSignal *>(self);
 
     // Native C++ signals can't be called like functions, thus we throw an exception.
     // The only way calling a signal can succeed (the Python equivalent of C++'s operator() )
@@ -793,16 +807,17 @@ static PyObject *_getHomonymousMethod(PySideSignalInstance *inst)
     // but walk through the whole mro to find a hidden method with the same name.
     auto signalName = inst->d->signalName;
     Shiboken::AutoDecRef name(Shiboken::String::fromCString(signalName));
-    auto *mro = Py_TYPE(inst->d->source)->tp_mro;
-    const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+    auto *mro = Py_TYPE(inst->d->shared->source)->tp_mro;
+    const Py_ssize_t n = PyTuple_Size(mro);
 
     for (Py_ssize_t idx = 0; idx < n; idx++) {
-        auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+        auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
         Shiboken::AutoDecRef tpDict(PepType_GetDict(sub_type));
         auto *hom = PyDict_GetItem(tpDict, name);
-        PyObject *realFunc{};
-        if (hom && PyCallable_Check(hom) && (realFunc = _getRealCallable(hom)))
-            return realFunc;
+        if (hom != nullptr && PyCallable_Check(hom) != 0) {
+            if (auto *realFunc = _getRealCallable(hom))
+                return realFunc;
+        }
     }
     return nullptr;
 }
@@ -817,7 +832,7 @@ static PyObject *signalInstanceCall(PyObject *self, PyObject *args, PyObject *kw
         return nullptr;
     }
 
-    Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->source,
+    Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->shared->source,
                                                                    nullptr));
     return PyObject_Call(homonymousMethod, args, kw);
 }
@@ -905,8 +920,9 @@ void updateSourceObject(PyObject *source)
 
     // PYSIDE-1431: Walk the mro and update. But see PYSIDE-1751 below.
     while ((mroItem.reset(PyIter_Next(mroIterator))), mroItem.object()) {
+        PyObject *key{};
+        PyObject *value{};
         Py_ssize_t pos = 0;
-        PyObject *key, *value;
         auto *type = reinterpret_cast<PyTypeObject *>(mroItem.object());
         Shiboken::AutoDecRef tpDict(PepType_GetDict(type));
         while (PyDict_Next(tpDict, &pos, &key, &value)) {
@@ -918,8 +934,10 @@ void updateSourceObject(PyObject *source)
                     auto *inst = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
                     Shiboken::AutoDecRef signalInstance(reinterpret_cast<PyObject *>(inst));
                     auto *si = reinterpret_cast<PySideSignalInstance *>(signalInstance.object());
+                    auto shared = std::make_shared<PySideSignalInstanceShared>();
+                    shared->source = source;
                     instanceInitialize(si, key, reinterpret_cast<PySideSignal *>(value),
-                                       source, 0);
+                                       shared, 0);
                     if (PyDict_SetItem(dict, key, signalInstance) == -1)
                         return;     // An error occurred while setting the attribute
                 }
@@ -931,6 +949,23 @@ void updateSourceObject(PyObject *source)
         return;
 }
 
+// PYSIDE-2840: For an enum registered in Qt, return the C++ name.
+// Ignore flags here; their underlying enums are of Python type flags anyways.
+static QByteArray getQtEnumTypeName(PyTypeObject *type)
+{
+    if (!Shiboken::Enum::checkType(type))
+        return {};
+
+    Shiboken::AutoDecRef qualName(PyObject_GetAttr(reinterpret_cast<PyObject *>(type),
+                                                   Shiboken::PyMagicName::qualname()));
+    QByteArray result = Shiboken::String::toCString(qualName.object());
+    result.replace(".", "::");
+
+    const auto metaType = QMetaType::fromName(result);
+    return metaType.isValid() && metaType.flags().testFlag(QMetaType::IsEnumeration)
+        ? result : QByteArray{};
+}
+
 QByteArray getTypeName(PyObject *obType)
 {
     if (PyType_Check(obType)) {
@@ -950,7 +985,8 @@ QByteArray getTypeName(PyObject *obType)
             return QByteArrayLiteral("QVariantList");
         if (type == &PyDict_Type)
             return QByteArrayLiteral("QVariantMap");
-        return QByteArrayLiteral("PyObject");
+        QByteArray enumName = getQtEnumTypeName(type);
+        return enumName.isEmpty() ? "PyObject"_ba : enumName;
     }
     if (obType == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None
         return voidType();
@@ -960,7 +996,7 @@ QByteArray getTypeName(PyObject *obType)
             result = sizeof(qreal) == sizeof(double) ? "double" : "float";
         return result;
     }
-    return QByteArray();
+    return {};
 }
 
 static QByteArray buildSignature(const QByteArray &name, const QByteArray &signature)
@@ -992,21 +1028,31 @@ static PySideSignalData::Signature parseSignature(PyObject *args)
 
 static void sourceGone(void *data)
 {
-    auto *self = reinterpret_cast<PySideSignalInstance *>(data);
-    self->deleted = true;
+    auto *ptr = reinterpret_cast<PySideSignalInstanceSharedPtr *>(data);
+    (*ptr)->deleted = true;
+    delete ptr;
 }
 
-static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySideSignal *signal, PyObject *source, int index)
+static void instanceInitialize(PySideSignalInstance *self, PyObject *name,
+                               PySideSignal *signal,
+                               const PySideSignalInstanceSharedPtr &shared,
+                               int index)
 {
     self->d = new PySideSignalInstancePrivate;
-    self->deleted = false;
     PySideSignalInstancePrivate *selfPvt = self->d;
+    selfPvt->shared = shared;
+    // PYSIDE-2201: We have no reference to source. Let's take a weakref to get
+    //              notified when source gets deleted.
+    if (index == 0) {
+        auto *ptr = new PySideSignalInstanceSharedPtr(shared);
+        PySide::WeakRef::create(shared->source, sourceGone, ptr);
+    }
+
     selfPvt->next = nullptr;
     if (signal->data->signalName.isEmpty())
         signal->data->signalName = Shiboken::String::toCString(name);
     selfPvt->signalName = signal->data->signalName;
 
-    selfPvt->source = source;
     const auto &signature = signal->data->signatures.at(index);
     selfPvt->signature = buildSignature(self->d->signalName, signature.signature);
     selfPvt->argCount = signature.argCount;
@@ -1016,15 +1062,12 @@ static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySid
         selfPvt->homonymousMethod = signal->homonymousMethod;
         Py_INCREF(selfPvt->homonymousMethod);
     }
-    // PYSIDE-2201: We have no reference to source. Let's take a weakref to get
-    //              notified when source gets deleted.
-    PySide::WeakRef::create(source, sourceGone, self);
 
     index++;
 
     if (index < signal->data->signatures.size()) {
         selfPvt->next = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
-        instanceInitialize(selfPvt->next, name, signal, source, index);
+        instanceInitialize(selfPvt->next, name, signal, shared, index);
     }
 }
 
@@ -1041,8 +1084,10 @@ PySideSignalInstance *initialize(PySideSignal *self, PyObject *name, PyObject *o
 
     PySideSignalInstance *instance = PyObject_New(PySideSignalInstance,
                                                   PySideSignalInstance_TypeF());
-    instanceInitialize(instance, name, self, object, 0);
-    auto sbkObj = reinterpret_cast<SbkObject *>(object);
+    auto shared = std::make_shared<PySideSignalInstanceShared>();
+    shared->source = object;
+    instanceInitialize(instance, name, self, shared, 0);
+    auto *sbkObj = reinterpret_cast<SbkObject *>(object);
     if (!Shiboken::Object::wasCreatedByPython(sbkObj))
         Py_INCREF(object); // PYSIDE-79: this flag was crucial for a wrapper call.
     return instance;
@@ -1070,6 +1115,8 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet
 {
     PySideSignalInstance *root = nullptr;
     PySideSignalInstance *previous = nullptr;
+    auto shared = std::make_shared<PySideSignalInstanceShared>();
+    shared->source = source;
     for (const QMetaMethod &m : methodList) {
         PySideSignalInstance *item = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
         if (!root)
@@ -1079,15 +1126,14 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet
             previous->d->next = item;
 
         item->d = new PySideSignalInstancePrivate;
-        item->deleted = false;
         PySideSignalInstancePrivate *selfPvt = item->d;
-        selfPvt->source = source;
+        selfPvt->shared = shared;
         QByteArray cppName(m.methodSignature());
         cppName.truncate(cppName.indexOf('('));
         // separate SignalName
         selfPvt->signalName = cppName;
         selfPvt->signature = m.methodSignature();
-        selfPvt->argCount = int(m.parameterCount());
+        selfPvt->argCount = short(m.parameterCount());
         selfPvt->attributes = m.attributes();
         selfPvt->homonymousMethod = nullptr;
         selfPvt->next = nullptr;
@@ -1098,9 +1144,8 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet
 static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalName, PySideSignal *signal)
 {
     Shiboken::AutoDecRef tpDict(PepType_GetDict(wrapperType));
-    auto typeDict = tpDict.object();
-    PyObject *homonymousMethod;
-    if ((homonymousMethod = PyDict_GetItemString(typeDict, signalName))) {
+    auto *typeDict = tpDict.object();
+    if (auto *homonymousMethod = PyDict_GetItemString(typeDict, signalName)) {
         Py_INCREF(homonymousMethod);
         signal->homonymousMethod = homonymousMethod;
     }
@@ -1168,7 +1213,7 @@ void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
 
 PyObject *getObject(PySideSignalInstance *signal)
 {
-    return signal->d->source;
+    return signal->d->shared->source;
 }
 
 const char *getSignature(PySideSignalInstance *signal)
@@ -1181,35 +1226,13 @@ EmitterData getEmitterData(PySideSignalInstance *signal)
     EmitterData result;
     result.emitter = PySide::convertToQObject(getObject(signal), false);
     if (result.emitter != nullptr) {
-        auto *mo = result.emitter->metaObject();
+        const auto *mo = result.emitter->metaObject();
         result.methodIndex = mo->indexOfMethod(getSignature(signal));
     }
     return result;
 }
 
-QByteArrayList getArgsFromSignature(const char *signature, bool *isShortCircuit)
-{
-    QByteArray qsignature = QByteArray(signature).trimmed();
-    QByteArrayList result;
-
-    if (isShortCircuit)
-        *isShortCircuit = !qsignature.contains(u'(');
-    if (qsignature.contains("()") || qsignature.contains("(void)"))
-        return result;
-    if (qsignature.endsWith(')')) {
-        const auto paren = qsignature.indexOf('(');
-        if (paren >= 0) {
-            qsignature.chop(1);
-            qsignature.remove(0, paren + 1);
-            result = qsignature.split(u',');
-            for (auto &type : result)
-                type = type.trimmed();
-        }
-    }
-    return result;
-}
-
-QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+QByteArray getCallbackSignature(QMetaMethod signal, QObject *receiver,
                                 PyObject *callback, bool encodeName)
 {
     QByteArray functionName;
@@ -1243,7 +1266,7 @@ QByteArray getCallbackSignature(const char *signal, QObject *receiver,
         }
 #endif
     } else if (PyCFunction_Check(callback)) {
-        const PyCFunctionObject *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback);
+        const auto *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback);
         functionName = PepCFunction_GET_NAMESTR(funcObj);
         useSelf = PyCFunction_GET_SELF(funcObj) != nullptr ? 1 : 0;
         const int flags = PyCFunction_GET_FLAGS(funcObj);
@@ -1256,7 +1279,7 @@ QByteArray getCallbackSignature(const char *signal, QObject *receiver,
             for (int i = 0, count = mo->methodCount(); i < count; ++i) {
                 QMetaMethod me = mo->method(i);
                 if ((strncmp(me.methodSignature(), prefix, prefix.size()) == 0) &&
-                    QMetaObject::checkConnectArgs(signal, me.methodSignature())) {
+                    QMetaObject::checkConnectArgs(signal, me)) {
                     numArgs = me.parameterTypes().size() + useSelf;
                     break;
                 }
@@ -1277,24 +1300,26 @@ QByteArray getCallbackSignature(const char *signal, QObject *receiver,
         functionName = Shiboken::String::toCString(slotArgs.functionName);
     Q_ASSERT(!functionName.isEmpty());
 
-    bool isShortCircuit = false;
-
     if (functionName.startsWith('<') && functionName.endsWith('>')) { // fix "<lambda>"
         functionName[0] = '_';
         functionName[functionName.size() - 1] = '_';
     }
     QByteArray signature = encodeName ? codeCallbackName(callback, functionName) : functionName;
-    QByteArrayList args = getArgsFromSignature(signal, &isShortCircuit);
-
-    if (!isShortCircuit) {
-        signature.append(u'(');
-        if (numArgs == -1)
-            numArgs = std::numeric_limits<qsizetype>::max();
-        while (!args.isEmpty() && (args.size() > (numArgs - useSelf)))
-            args.removeLast();
-        signature.append(args.join(','));
-        signature.append(')');
+    QByteArrayList args = signal.parameterTypes();
+
+    signature.append(u'(');
+
+    int slotArgumentCount = signal.parameterCount();
+    if (numArgs != -1 && slotArgumentCount > (numArgs - useSelf))
+        slotArgumentCount = numArgs - useSelf;
+
+    for (int i = 0; i < slotArgumentCount; ++i) {
+        if (i > 0)
+            signature.append(',');
+        signature.append(signal.parameterTypeName(i));
     }
+    signature.append(')');
+
     return signature;
 }
 
index 589fc2c92ef817a54cb400807b684aee2e816842..d1f3674700bc5143dbe2559aa51513fdeb701ee4 100644 (file)
@@ -30,7 +30,6 @@ extern "C"
     {
         PyObject_HEAD
         PySideSignalInstancePrivate *d;
-        bool deleted;
     };
 }; // extern "C"
 
@@ -135,20 +134,8 @@ PYSIDE_API bool checkQtSignal(const char *signature);
  * @param   encodeName  Used to specify if the returned signature will be encoded with Qt signal/slot style
  * @return  Return the callback signature
  **/
-PYSIDE_API QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+PYSIDE_API QByteArray getCallbackSignature(QMetaMethod signal, QObject *receiver,
                                            PyObject *callback, bool encodeName);
-
-/**
- * This function parses the signature and then returns a list of argument types.
- *
- * @param   signature       The signal signature
- * @param   isShortCircuit  If this is a shortCircuit(python<->python) signal
- * @return  Return true if this is a Qt Signal, otherwise return false
- * @todo    replace return type by QList<QByteArray>
- **/
-QByteArrayList getArgsFromSignature(const char *signature,
-                                    bool *isShortCircuit = nullptr);
-
 } // namespace PySide::Signal
 
 #endif
index 55a9a7a704ec7182a9440341cb1391f314774ee9..259da0f3470a6c6440429f0de14be18db306ae77 100644 (file)
@@ -9,6 +9,8 @@
 #include <QtCore/QByteArray>
 #include <QtCore/QList>
 
+#include <memory>
+
 struct PySideSignalData
 {
     struct Signature
@@ -36,11 +38,19 @@ extern "C"
     struct PySideSignalInstance;
 }; //extern "C"
 
+struct PySideSignalInstanceShared
+{
+    PyObject *source = nullptr;
+    bool deleted = false;
+};
+
+using PySideSignalInstanceSharedPtr = std::shared_ptr<PySideSignalInstanceShared>;
+
 struct PySideSignalInstancePrivate
 {
     QByteArray signalName;
     QByteArray signature;
-    PyObject *source = nullptr;
+    PySideSignalInstanceSharedPtr shared;
     PyObject *homonymousMethod = nullptr;
     PySideSignalInstance *next = nullptr;
     unsigned short attributes = 0;
index fa7e89f42b65e5bab7796ce49ca5ed1f1809022e..5232b4e4c599286216969aa68b14c3618a29e835 100644 (file)
@@ -84,11 +84,11 @@ int slotTpInit(PyObject *self, PyObject *args, PyObject *kw)
         return -1;
     }
 
-    PySideSlot *data = reinterpret_cast<PySideSlot *>(self);
+    auto *data = reinterpret_cast<PySideSlot *>(self);
     if (!data->slotData)
         data->slotData = new SlotData;
     for(Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) {
-        PyObject *argType = PyTuple_GET_ITEM(args, i);
+        PyObject *argType = PyTuple_GetItem(args, i);
         const auto typeName = PySide::Signal::getTypeName(argType);
         if (typeName.isEmpty()) {
             PyErr_Format(PyExc_TypeError, "Unknown signal argument type: %s", Py_TYPE(argType)->tp_name);
@@ -120,7 +120,7 @@ PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */)
     Py_INCREF(callback);
 
     if (PyCallable_Check(callback)) {
-        PySideSlot *data = reinterpret_cast<PySideSlot *>(self);
+        auto *data = reinterpret_cast<PySideSlot *>(self);
 
         if (!data->slotData)
             data->slotData = new SlotData;
@@ -168,7 +168,7 @@ DataList *dataListFromCapsule(PyObject *capsule)
 
 static const char *Slot_SignatureStrings[] = {
     "PySide6.QtCore.Slot(self,*types:type,name:str=nullptr,result:type=nullptr)",
-    "PySide6.QtCore.Slot.__call__(self,function:typing.Callable)->typing.Any",
+    "PySide6.QtCore.Slot.__call__(self,function:typing.Callable[[typing.Any],typing.Any])->typing.Any",
     nullptr}; // Sentinel
 
 void init(PyObject *module)
index 5f3ca59e413315d7264dd10d99dc6cc012323d59..48f9b3d032e08c52c3c852ca4a430c4399c1bb8c 100644 (file)
@@ -42,10 +42,10 @@ static PyTypeObject *PySideCallableObject_TypeF()
 
 static PyObject *CallableObject_call(PyObject *callable_object, PyObject *args, PyObject * /* kw */)
 {
-    PySideCallableObject *obj = reinterpret_cast<PySideCallableObject *>(callable_object);
+    auto *obj = reinterpret_cast<PySideCallableObject *>(callable_object);
     obj->weakref_func(obj->user_data);
 
-    Py_XDECREF(PyTuple_GET_ITEM(args, 0)); //kill weak ref object
+    Py_XDECREF(PyTuple_GetItem(args, 0)); //kill weak ref object
     Py_RETURN_NONE;
 }
 
index 3c5b759533be038c6f9b43288bb27af99c685fa2..b3b0633fafad60e6afe6a4ef1505743e6f6f1edd 100644 (file)
@@ -2,13 +2,15 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
 #include "qobjectconnect.h"
+#include "dynamicslot_p.h"
 #include "pysideqobject.h"
 #include "pysideqslotobject_p.h"
 #include "pysidesignal.h"
 #include "pysideutils.h"
 #include "signalmanager.h"
 
-#include "shiboken.h"
+#include <sbkstring.h>
+#include <sbkstaticstrings.h>
 #include "basewrapper.h"
 #include "autodecref.h"
 
@@ -29,7 +31,7 @@ static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self
 
     // PYSIDE-1523: Each could be a compiled method or a normal method here, for the
     // compiled ones we can use the attributes.
-    PyObject *function1;
+    PyObject *function1{};
     if (PyMethod_Check(otherMethod.object())) {
         function1 = PyMethod_GET_FUNCTION(otherMethod.object());
     } else {
@@ -40,7 +42,7 @@ static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self
         // Not retaining a reference in line with what PyMethod_GET_FUNCTION does.
     }
 
-    PyObject *function2;
+    PyObject *function2{};
     if (is_pymethod) {
         function2 = PyMethod_GET_FUNCTION(method);
     } else {
@@ -57,7 +59,7 @@ struct GetReceiverResult
     QObject *receiver = nullptr;
     PyObject *self = nullptr;
     QByteArray callbackSig;
-    bool usingGlobalReceiver = false;
+    bool forceDynamicSlot = false;
     int slotIndex = -1;
 };
 
@@ -68,8 +70,8 @@ QDebug operator<<(QDebug d, const GetReceiverResult &r)
     d.noquote();
     d.nospace();
     d << "GetReceiverResult(receiver=" << r.receiver << ", self=" << r.self
-      << ", sig=\"" << r.callbackSig << "\", slotIndex=" << r.slotIndex
-      << ", usingGlobalReceiver=" << r.usingGlobalReceiver << ')';
+      << ", forceDynamicSlot=" << r.forceDynamicSlot
+      << ", sig=\"" << r.callbackSig << "\", slotIndex=" << r.slotIndex << ')';
     return d;
 }
 #endif // QT_NO_DEBUG_STREAM
@@ -85,7 +87,7 @@ static const char *getQualifiedName(PyObject *ob)
 static bool isDeclaredIn(PyObject *method, const char *className)
 {
     bool result = false;
-    if (auto *qualifiedNameC = getQualifiedName(PyMethod_Function(method))) {
+    if (const auto *qualifiedNameC = getQualifiedName(PyMethod_Function(method))) {
         std::string_view qualifiedName(qualifiedNameC);
         if (const auto dot = qualifiedName.rfind('.'); dot != std::string::npos)
             result = qualifiedName.substr(0, dot) == className;
@@ -93,16 +95,15 @@ static bool isDeclaredIn(PyObject *method, const char *className)
     return result;
 }
 
-static GetReceiverResult getReceiver(QObject *source, const char *signal,
-                                     PyObject *callback)
+static GetReceiverResult getReceiver(QMetaMethod signal, PyObject *callback)
 {
     GetReceiverResult result;
 
-    bool forceGlobalReceiver = false;
     if (PyMethod_Check(callback)) {
         result.self = PyMethod_GET_SELF(callback);
         result.receiver = PySide::convertToQObject(result.self, false);
-        forceGlobalReceiver = isMethodDecorator(callback, true, result.self);
+        // Prevent dynamic slot creation for decorators
+        result.forceDynamicSlot = isMethodDecorator(callback, true, result.self);
 #ifdef PYPY_VERSION
     } else if (Py_TYPE(callback) == PepBuiltinMethod_TypePtr) {
         result.self = PyObject_GetAttrString(callback, "__self__");
@@ -116,23 +117,23 @@ static GetReceiverResult getReceiver(QObject *source, const char *signal,
         result.self = PyObject_GetAttr(callback, Shiboken::PyName::im_self());
         Py_DECREF(result.self);
         result.receiver = PySide::convertToQObject(result.self, false);
-        forceGlobalReceiver = isMethodDecorator(callback, false, result.self);
+        // Prevent dynamic slot creation for decorators
+        result.forceDynamicSlot = isMethodDecorator(callback, false, result.self);
     } else if (PyCallable_Check(callback)) {
         // Ok, just a callable object
         result.receiver = nullptr;
         result.self = nullptr;
     }
 
-    result.usingGlobalReceiver = !result.receiver || forceGlobalReceiver;
+    result.callbackSig =
+        PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
+                                             false);
 
     // Check if this callback is a overwrite of a non-virtual Qt slot (pre-Jira bug 1019).
     // Make it possible to connect to a MyWidget.show() although QWidget.show()
     // is a non-virtual slot which would be found by QMetaObject search.
     // FIXME PYSIDE7: This is arguably a bit of a misguided "feature", remove?
-    if (!result.usingGlobalReceiver && result.receiver && result.self) {
-        result.callbackSig =
-            PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
-                                                 result.usingGlobalReceiver);
+    if (result.receiver && result.self) {
         const QMetaObject *metaObject = result.receiver->metaObject();
         result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
         if (PyMethod_Check(callback) != 0 && result.slotIndex != -1
@@ -142,27 +143,13 @@ static GetReceiverResult getReceiver(QObject *source, const char *signal,
                  metaObject = metaObject->superClass();
              // If the Python callback is not declared in the same class, assume it is
              // a Python override. Resort to global receiver (PYSIDE-2418).
-             if (!isDeclaredIn(callback, metaObject->className()))
-                 result.usingGlobalReceiver = true;
+             if (!isDeclaredIn(callback, metaObject->className())) {
+                 result.receiver = nullptr;
+                 result.slotIndex = -1;
+             }
          }
     }
 
-    const auto receiverThread = result.receiver ? result.receiver->thread() : nullptr;
-
-    if (result.usingGlobalReceiver) {
-        PySide::SignalManager &signalManager = PySide::SignalManager::instance();
-        result.receiver = signalManager.globalReceiver(source, callback, result.receiver);
-        // PYSIDE-1354: Move the global receiver to the original receivers's thread
-        // so that autoconnections work correctly.
-        if (receiverThread && receiverThread != result.receiver->thread())
-            result.receiver->moveToThread(receiverThread);
-        result.callbackSig =
-            PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
-                                                 result.usingGlobalReceiver);
-        const QMetaObject *metaObject = result.receiver->metaObject();
-        result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
-    }
-
     return result;
 }
 
@@ -212,52 +199,39 @@ QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *sign
         return {};
 
     // Extract receiver from callback
-    const GetReceiverResult receiver = getReceiver(source, signal + 1, callback);
-    if (receiver.receiver == nullptr && receiver.self == nullptr)
-        return {};
-
-    int slotIndex = receiver.slotIndex;
-
-    PySide::SignalManager &signalManager = PySide::SignalManager::instance();
-    if (slotIndex == -1) {
-        if (!receiver.usingGlobalReceiver && receiver.self
-            && !Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(receiver.self))) {
-            qWarning("You can't add dynamic slots on an object originated from C++.");
-            if (receiver.usingGlobalReceiver)
-                signalManager.releaseGlobalReceiver(source, receiver.receiver);
-
-            return {};
-        }
-
-        const char *slotSignature = receiver.callbackSig.constData();
-        slotIndex = receiver.usingGlobalReceiver
-            ? signalManager.globalReceiverSlotIndex(receiver.receiver, slotSignature)
-            : PySide::SignalManager::registerMetaMethodGetIndex(receiver.receiver, slotSignature,
-                                                                QMetaMethod::Slot);
-
-        if (slotIndex == -1) {
-            if (receiver.usingGlobalReceiver)
-                signalManager.releaseGlobalReceiver(source, receiver.receiver);
-
-            return {};
-        }
+    const QMetaMethod signalMethod = source->metaObject()->method(signalIndex);
+    GetReceiverResult receiver = getReceiver(signalMethod, callback);
+    if (!receiver.forceDynamicSlot && receiver.receiver != nullptr && receiver.slotIndex == -1) {
+        receiver.slotIndex = PySide::SignalManager::registerMetaMethodGetIndexBA(receiver.receiver,
+                                                                                 receiver.callbackSig,
+                                                                                 QMetaMethod::Slot);
     }
 
     QMetaObject::Connection connection{};
     Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
-    connection = QMetaObject::connect(source, signalIndex, receiver.receiver, slotIndex, type);
+    if (!receiver.forceDynamicSlot && receiver.receiver != nullptr && receiver.slotIndex != -1) {
+        connection = QMetaObject::connect(source, signalIndex,
+                                          receiver.receiver, receiver.slotIndex, type);
+    } else {
+        auto parameterTypes = signalMethod.parameterTypes();
+        // Slots might have fewer arguments.
+        if (!receiver.callbackSig.isEmpty()) {
+            const auto paramCount = receiver.callbackSig.endsWith("()")
+                                    ? qsizetype(0) : receiver.callbackSig.count(',') + 1;
+            if (parameterTypes.size() > paramCount)
+                parameterTypes.resize(paramCount);
+        }
+        auto *slotObject = new PySideQSlotObject(callback,
+                                                 parameterTypes,
+                                                 signalMethod.typeName());
+        connection = QObjectPrivate::connect(source, signalIndex, slotObject, type);
+    }
     Py_END_ALLOW_THREADS
-    if (!connection) {
-        if (receiver.usingGlobalReceiver)
-            signalManager.releaseGlobalReceiver(source, receiver.receiver);
+    if (!connection)
         return {};
-    }
 
-    Q_ASSERT(receiver.receiver);
-    if (receiver.usingGlobalReceiver)
-        signalManager.notifyGlobalReceiver(receiver.receiver);
+    registerSlotConnection(source, signalIndex, callback, connection);
 
-    const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex);
     static_cast<FriendlyQObject *>(source)->connectNotify(signalMethod);
     return connection;
 }
@@ -274,30 +248,18 @@ QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *sign
     if (signalIndex == -1)
         return {};
 
-    // Extract receiver from callback
-    const GetReceiverResult receiver = getReceiver(source, signal + 1, callback);
-    if (receiver.receiver == nullptr && receiver.self == nullptr)
-        return {};
-
-    PySide::SignalManager &signalManager = PySide::SignalManager::instance();
-
-    PySideQSlotObject *slotObject = new PySideQSlotObject(callback);
+    const QMetaMethod signalMethod = source->metaObject()->method(signalIndex);
+    auto *slotObject = new PySideQSlotObject(callback,
+                                             signalMethod.parameterTypes(),
+                                             signalMethod.typeName());
 
     QMetaObject::Connection connection{};
     Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
     connection = QObjectPrivate::connect(source, signalIndex, context, slotObject, type);
     Py_END_ALLOW_THREADS
-    if (!connection) {
-        if (receiver.usingGlobalReceiver)
-            signalManager.releaseGlobalReceiver(source, receiver.receiver);
+    if (!connection)
         return {};
-    }
-
-    Q_ASSERT(receiver.receiver);
-    if (receiver.usingGlobalReceiver)
-        signalManager.notifyGlobalReceiver(receiver.receiver);
 
-    const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex);
     static_cast<FriendlyQObject *>(source)->connectNotify(signalMethod);
     return connection;
 }
@@ -307,30 +269,35 @@ bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *ca
     if (!PySide::Signal::checkQtSignal(signal))
         return false;
 
-    // Extract receiver from callback
-    const GetReceiverResult receiver = getReceiver(nullptr, signal, callback);
-    if (receiver.receiver == nullptr && receiver.self == nullptr)
+    const auto *metaObject = source->metaObject();
+    const int signalIndex = metaObject->indexOfSignal(signal + 1);
+    if (signalIndex == -1)
         return false;
 
-    const int signalIndex = source->metaObject()->indexOfSignal(signal + 1);
-    const int slotIndex = receiver.slotIndex;
-
-    bool ok{};
-    Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with disconnectNotify()
-    ok = QMetaObject::disconnectOne(source, signalIndex, receiver.receiver, slotIndex);
-    Py_END_ALLOW_THREADS
-    if (!ok)
+    if (!disconnectSlot(source, signalIndex, callback))
         return false;
 
-    Q_ASSERT(receiver.receiver);
-    const QMetaMethod slotMethod = receiver.receiver->metaObject()->method(slotIndex);
-    static_cast<FriendlyQObject *>(source)->disconnectNotify(slotMethod);
+    const QMetaMethod signalMethod = metaObject->method(signalIndex);
+    static_cast<FriendlyQObject *>(source)->disconnectNotify(signalMethod);
+    return true;
+}
+
+bool callConnect(PyObject *self, const char *signal, PyObject *argument)
+{
+    using Shiboken::AutoDecRef;
 
-    if (receiver.usingGlobalReceiver) { // might delete the receiver
-        PySide::SignalManager &signalManager = PySide::SignalManager::instance();
-        signalManager.releaseGlobalReceiver(source, receiver.receiver);
+    if (PyObject_TypeCheck(argument, PySideSignalInstance_TypeF()) == 0) {
+        AutoDecRef result(PyObject_CallMethod(self, "connect", "OsO", self, signal, argument));
+        return !result.isNull();
     }
-    return true;
+
+    // Connecting signal to signal
+    auto *signalInstance = reinterpret_cast<PySideSignalInstance *>(argument);
+    AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
+    AutoDecRef result(PyObject_CallMethod(self, "connect", "OsOO", self, signal,
+                                          PySide::Signal::getObject(signalInstance),
+                                          signalSignature.object()));
+    return !result.isNull();
 }
 
 } // namespace PySide
index c99b8006ed216398cdaeceafcf851a954dd1336f..66359ae672d613c959b2245705801ffc66800fb3 100644 (file)
@@ -42,6 +42,10 @@ PYSIDE_API QMetaObject::Connection
 PYSIDE_API bool qobjectDisconnectCallback(QObject *source, const char *signal,
                                           PyObject *callback);
 
+/// Helper for functions that forward arguments to QObject::connect(),
+/// for example, QTimer::singleShot().
+PYSIDE_API bool callConnect(PyObject *self, const char *signal, PyObject *argument);
+
 } // namespace PySide
 
 #endif // QOBJECTCONNECT_H
index 557f130e04e9af366b267e8146cdbc876b6a38f0..fe99342920d524e864a91ca7a9d43883f51a12ad 100644 (file)
@@ -6,11 +6,9 @@
 #include "pysidelogging_p.h"
 #include "pysideproperty.h"
 #include "pysideproperty_p.h"
-#include "pysidecleanup.h"
 #include "pyside_p.h"
 #include "dynamicqmetaobject.h"
 #include "pysidemetafunction_p.h"
-#include "pysidestaticstrings.h"
 
 #include <autodecref.h>
 #include <basewrapper.h>
@@ -28,8 +26,6 @@
 #include <QtCore/QScopedPointer>
 #include <QtCore/QTimerEvent>
 
-#include <algorithm>
-#include <limits>
 #include <memory>
 
 using namespace Qt::StringLiterals;
@@ -37,20 +33,13 @@ using namespace Qt::StringLiterals;
 #if QSLOT_CODE != 1 || QSIGNAL_CODE != 2
 #error QSLOT_CODE and/or QSIGNAL_CODE changed! change the hardcoded stuff to the correct value!
 #endif
-#define PYSIDE_SLOT '1'
-#define PYSIDE_SIGNAL '2'
-#include "globalreceiverv2.h"
 
 static PyObject *metaObjectAttr = nullptr;
-static PyObject *parseArguments(const QMetaMethod &method, void **args);
-static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args);
-
-static bool qAppRunning = false;
 
 static void destroyMetaObject(PyObject *obj)
 {
     void *ptr = PyCapsule_GetPointer(obj, nullptr);
-    auto meta = reinterpret_cast<PySide::MetaObjectBuilder *>(ptr);
+    auto *meta = reinterpret_cast<PySide::MetaObjectBuilder *>(ptr);
     SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta);
     if (wrapper)
         Shiboken::BindingManager::instance().releaseWrapper(wrapper);
@@ -78,7 +67,7 @@ static const char *metaCallName(QMetaObject::Call call)
 static QByteArray methodSignature(const QMetaMethod &method)
 {
     QByteArray result;
-    if (auto *t = method.typeName()) {
+    if (const auto *t = method.typeName()) {
         result += t;
         result += ' ';
     }
@@ -86,16 +75,28 @@ static QByteArray methodSignature(const QMetaMethod &method)
     return result;
 }
 
-static QByteArray msgCannotConvertParameter(const QMetaMethod &method, qsizetype p)
+static QByteArray msgCannotConvertParameter(const QByteArray &parameterTypeName,
+                                            const QByteArray &signature, qsizetype p)
 {
-    return "Cannot call meta function \""_ba + methodSignature(method)
+    return "Cannot call meta function \""_ba + signature
            + "\" because parameter " + QByteArray::number(p) + " of type \""_ba
-           + method.parameterTypeName(p) + "\" cannot be converted."_ba;
+           + parameterTypeName + "\" cannot be converted."_ba;
+}
+
+static inline QByteArray msgCannotConvertParameter(QMetaMethod method, qsizetype p)
+{
+    return msgCannotConvertParameter(method.parameterTypeName(p),
+                                     methodSignature(method), p);
 }
 
-static QByteArray msgCannotConvertReturn(const QMetaMethod &method)
+static QByteArray msgCannotConvertReturn(const QByteArray &signature)
 {
-    return "The return value of \""_ba + methodSignature(method) + "\" cannot be converted."_ba;
+    return "The return value of \""_ba + signature + "\" cannot be converted."_ba;
+}
+
+static inline QByteArray msgCannotConvertReturn(QMetaMethod method)
+{
+    return msgCannotConvertReturn(methodSignature(method));
 }
 
 namespace PySide {
@@ -183,8 +184,8 @@ QDataStream &operator<<(QDataStream &out, const PyObjectWrapper &myObj)
         const char *buff = nullptr;
         Py_ssize_t size  = 0;
         if (PyBytes_Check(repr.object())) {
-            buff = PyBytes_AS_STRING(repr.object());
-            size = PyBytes_GET_SIZE(repr.object());
+            buff = PyBytes_AsString(repr.object());
+            size = PyBytes_Size(repr.object());
         } else if (Shiboken::String::check(repr.object())) {
             buff = Shiboken::String::toCString(repr.object());
             size = Shiboken::String::len(repr.object());
@@ -222,75 +223,20 @@ QDataStream &operator>>(QDataStream &in, PyObjectWrapper &myObj)
 
 };
 
-namespace PySide {
-using GlobalReceiverV2Ptr = std::shared_ptr<GlobalReceiverV2>;
-using GlobalReceiverV2Map = QHash<PySide::GlobalReceiverKey, GlobalReceiverV2Ptr>;
-}
-
 using namespace PySide;
 
-// Listen for destroy() of main thread objects and ensure cleanup
-class SignalManagerDestroyListener : public QObject
-{
-    Q_OBJECT
-public:
-    Q_DISABLE_COPY_MOVE(SignalManagerDestroyListener)
-
-    using QObject::QObject;
-
-public Q_SLOTS:
-    void destroyNotify(const QObject *);
-
-protected:
-    void timerEvent(QTimerEvent *event) override;
-
-private:
-    int m_timerId = -1;
-};
-
-void SignalManagerDestroyListener::destroyNotify(const QObject *)
+struct SignalManagerPrivate
 {
-    if (qAppRunning && m_timerId == -1)
-        m_timerId = startTimer(0);
-}
-
-void SignalManagerDestroyListener::timerEvent(QTimerEvent *event)
-{
-    if (event->timerId() == m_timerId) {
-        killTimer(std::exchange(m_timerId, -1));
-        SignalManager::instance().purgeEmptyGlobalReceivers();
-    }
-}
-
-struct SignalManager::SignalManagerPrivate
-{
-    Q_DISABLE_COPY_MOVE(SignalManagerPrivate)
-
-    SignalManagerPrivate() noexcept = default;
-    ~SignalManagerPrivate() { clear(); }
-
-    void deleteGlobalReceiver(const QObject *gr);
-    void clear();
-    void purgeEmptyGlobalReceivers();
-
-    GlobalReceiverV2Map m_globalReceivers;
     static SignalManager::QmlMetaCallErrorHandler m_qmlMetaCallErrorHandler;
 
     static void handleMetaCallError(QObject *object, int *result);
     static int qtPropertyMetacall(QObject *object, QMetaObject::Call call,
                                   int id, void **args);
     static int qtMethodMetacall(QObject *object, int id, void **args);
-
-    QPointer<SignalManagerDestroyListener> m_listener;
 };
 
 SignalManager::QmlMetaCallErrorHandler
-    SignalManager::SignalManagerPrivate::m_qmlMetaCallErrorHandler = nullptr;
-
-static void clearSignalManager()
-{
-    PySide::SignalManager::instance().clear();
-}
+    SignalManagerPrivate::m_qmlMetaCallErrorHandler = nullptr;
 
 static void PyObject_PythonToCpp_PyObject_PTR(PyObject *pyIn, void *cppOut)
 {
@@ -302,13 +248,13 @@ static PythonToCppFunc is_PyObject_PythonToCpp_PyObject_PTR_Convertible(PyObject
 }
 static PyObject *PyObject_PTR_CppToPython_PyObject(const void *cppIn)
 {
-    auto pyOut = reinterpret_cast<PyObject *>(const_cast<void *>(cppIn));
+    auto *pyOut = reinterpret_cast<PyObject *>(const_cast<void *>(cppIn));
     if (pyOut)
         Py_INCREF(pyOut);
     return pyOut;
 }
 
-SignalManager::SignalManager() : m_d(new SignalManagerPrivate)
+void SignalManager::init()
 {
     // Register Qt primitive typedefs used on signals.
     using namespace Shiboken;
@@ -326,147 +272,15 @@ SignalManager::SignalManager() : m_d(new SignalManagerPrivate)
     Shiboken::Conversions::registerConverterName(converter, "PyObjectWrapper");
     Shiboken::Conversions::registerConverterName(converter, "PySide::PyObjectWrapper");
 
-    PySide::registerCleanupFunction(clearSignalManager);
-
     if (!metaObjectAttr)
         metaObjectAttr = Shiboken::String::fromCString("__METAOBJECT__");
 }
 
-void SignalManager::clear()
-{
-    m_d->clear();
-}
-
-SignalManager::~SignalManager()
-{
-    delete m_d;
-}
-
-SignalManager &SignalManager::instance()
-{
-    static SignalManager me;
-    return me;
-}
-
 void SignalManager::setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler)
 {
     SignalManagerPrivate::m_qmlMetaCallErrorHandler = handler;
 }
 
-static void qAppAboutToQuit()
-{
-    qAppRunning = false;
-    SignalManager::instance().purgeEmptyGlobalReceivers();
-}
-
-static bool isInMainThread(const QObject *o)
-{
-    if (o->isWidgetType() || o->isWindowType() || o->isQuickItemType())
-        return true;
-    auto *app = QCoreApplication::instance();
-    return app != nullptr && app->thread() == o->thread();
-}
-
-QObject *SignalManager::globalReceiver(QObject *sender, PyObject *callback, QObject *receiver)
-{
-    if (m_d->m_listener.isNull() && !QCoreApplication::closingDown()) {
-        if (auto *app = QCoreApplication::instance()) {
-            // The signal manager potentially outlives QCoreApplication, ensure deletion
-            m_d->m_listener = new SignalManagerDestroyListener(app);
-            m_d->m_listener->setObjectName("qt_pyside_signalmanagerdestroylistener");
-            QObject::connect(app, &QCoreApplication::aboutToQuit, qAppAboutToQuit);
-            qAppRunning = true;
-        }
-    }
-
-    auto &globalReceivers = m_d->m_globalReceivers;
-    const GlobalReceiverKey key = GlobalReceiverV2::key(callback);
-    auto it = globalReceivers.find(key);
-    if (it == globalReceivers.end()) {
-        auto gr = std::make_shared<GlobalReceiverV2>(callback, receiver);
-        it = globalReceivers.insert(key, gr);
-    }
-
-    if (sender != nullptr) {
-        it.value()->incRef(sender); // create a link reference
-
-        // For main thread-objects, add a notification for destroy (PYSIDE-2646, 2141)
-        if (qAppRunning && !m_d->m_listener.isNull() && isInMainThread(sender)) {
-            QObject::connect(sender, &QObject::destroyed,
-                             m_d->m_listener, &SignalManagerDestroyListener::destroyNotify,
-                             Qt::UniqueConnection);
-        }
-    }
-
-    return it.value().get();
-}
-
-void SignalManager::purgeEmptyGlobalReceivers()
-{
-    m_d->purgeEmptyGlobalReceivers();
-}
-
-void SignalManager::notifyGlobalReceiver(QObject *receiver)
-{
-    reinterpret_cast<GlobalReceiverV2 *>(receiver)->notify();
-    purgeEmptyGlobalReceivers();
-}
-
-void SignalManager::releaseGlobalReceiver(const QObject *source, QObject *receiver)
-{
-    auto gr = static_cast<GlobalReceiverV2 *>(receiver);
-    gr->decRef(source);
-    if (gr->isEmpty())
-        m_d->deleteGlobalReceiver(gr);
-}
-
-void SignalManager::deleteGlobalReceiver(const QObject *gr)
-{
-    SignalManager::instance().m_d->deleteGlobalReceiver(gr);
-}
-
-void SignalManager::SignalManagerPrivate::deleteGlobalReceiver(const QObject *gr)
-{
-    for (auto it = m_globalReceivers.begin(), end = m_globalReceivers.end(); it != end; ++it) {
-        if (it.value().get() == gr) {
-            m_globalReceivers.erase(it);
-            break;
-        }
-    }
-}
-
-void SignalManager::SignalManagerPrivate::clear()
-{
-    // Delete receivers by always retrieving the current first element,
-    // because deleting a receiver can indirectly delete another one
-    // via ~DynamicSlotDataV2(). Using ~QHash/clear() could cause an
-    // iterator invalidation, and thus undefined behavior.
-    while (!m_globalReceivers.isEmpty())
-        m_globalReceivers.erase(m_globalReceivers.cbegin());
-}
-
-static bool isEmptyGlobalReceiver(const GlobalReceiverV2Ptr &g)
-{
-    return g->isEmpty();
-}
-
-void SignalManager::SignalManagerPrivate::purgeEmptyGlobalReceivers()
-{
-    // Delete repetitively (see comment in clear()).
-    while (true) {
-        auto it = std::find_if(m_globalReceivers.cbegin(), m_globalReceivers.cend(),
-                               isEmptyGlobalReceiver);
-        if (it == m_globalReceivers.cend())
-            break;
-        m_globalReceivers.erase(it);
-    }
-}
-
-int SignalManager::globalReceiverSlotIndex(QObject *receiver, const char *signature) const
-{
-    return static_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature);
-}
-
 bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *args)
 {
     if (!Signal::checkQtSignal(signal))
@@ -474,20 +288,11 @@ bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *ar
     signal++;
 
     int signalIndex = source->metaObject()->indexOfSignal(signal);
-    if (signalIndex != -1) {
-        // cryptic but works!
-        // if the signature doesn't have a '(' it's a shor circuited signal, i.e. std::find
-        // returned the string null terminator.
-        bool isShortCircuit = !*std::find(signal, signal + std::strlen(signal), '(');
-        return isShortCircuit
-            ? emitShortCircuitSignal(source, signalIndex, args)
-            : MetaFunction::call(source, signalIndex, args);
-    }
-    return false;
+    return signalIndex != -1 && MetaFunction::call(source, signalIndex, args);
 }
 
 // Handle errors from meta calls. Requires GIL and PyErr_Occurred()
-void SignalManager::SignalManagerPrivate::handleMetaCallError(QObject *object, int *result)
+void SignalManagerPrivate::handleMetaCallError(QObject *object, int *result)
 {
     // Bubbles Python exceptions up to the Javascript engine, if called from one
     if (m_qmlMetaCallErrorHandler) {
@@ -495,7 +300,11 @@ void SignalManager::SignalManagerPrivate::handleMetaCallError(QObject *object, i
         if (idOpt.has_value())
             *result = idOpt.value();
     }
+    SignalManager::handleMetaCallError();
+}
 
+void SignalManager::handleMetaCallError()
+{
     const int reclimit = Py_GetRecursionLimit();
     // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
     // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
@@ -507,9 +316,9 @@ void SignalManager::SignalManagerPrivate::handleMetaCallError(QObject *object, i
 }
 
 // Handler for QMetaObject::ReadProperty/WriteProperty/ResetProperty:
-int SignalManager::SignalManagerPrivate::qtPropertyMetacall(QObject *object,
-                                                            QMetaObject::Call call,
-                                                            int id, void **args)
+int SignalManagerPrivate::qtPropertyMetacall(QObject *object,
+                                             QMetaObject::Call call,
+                                             int id, void **args)
 {
     const QMetaObject *metaObject = object->metaObject();
     int result = id - metaObject->propertyCount();
@@ -538,7 +347,9 @@ int SignalManager::SignalManagerPrivate::qtPropertyMetacall(QObject *object,
     if (PyErr_Occurred()) {
         // PYSIDE-2160: An unknown type was reported. Indicated by StopIteration.
         if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
-            PyObject *excType, *excValue, *excTraceback;
+            PyObject *excType{};
+            PyObject *excValue{};
+            PyObject *excTraceback{};
             PyErr_Fetch(&excType, &excValue, &excTraceback);
             bool ign = call == QMetaObject::WriteProperty;
             PyErr_WarnFormat(PyExc_RuntimeWarning, 0,
@@ -562,8 +373,8 @@ int SignalManager::SignalManagerPrivate::qtPropertyMetacall(QObject *object,
 }
 
 // Handler for QMetaObject::InvokeMetaMethod
-int SignalManager::SignalManagerPrivate::qtMethodMetacall(QObject *object,
-                                                          int id, void **args)
+int SignalManagerPrivate::qtMethodMetacall(QObject *object,
+                                           int id, void **args)
 {
     const QMetaObject *metaObject = object->metaObject();
     const QMetaMethod method = metaObject->method(id);
@@ -589,13 +400,13 @@ int SignalManager::SignalManagerPrivate::qtMethodMetacall(QObject *object,
             PyErr_Format(PyExc_AttributeError, "Slot '%s::%s' not found.",
                          metaObject->className(), method.methodSignature().constData());
         } else {
-            SignalManager::callPythonMetaMethod(method, args, pyMethod, false);
+            SignalManager::callPythonMetaMethod(method, args, pyMethod);
         }
     }
     // WARNING Isn't safe to call any metaObject and/or object methods beyond this point
     //         because the object can be deleted inside the called slot.
 
-    if (gil.get() == nullptr)
+    if (gil == nullptr)
         gil.reset(new Shiboken::GilState);
 
     if (PyErr_Occurred())
@@ -635,35 +446,114 @@ int SignalManager::qt_metacall(QObject *object, QMetaObject::Call call, int id,
     return id;
 }
 
-int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod, bool isShortCuit)
+// Helper for calling a Python pyCallable matching a Qt signal / slot.
+enum CallResult : int
 {
-    Q_ASSERT(pyMethod);
+    CallOk,
+    CallOtherError, // Python error set
+    CallReturnValueError,
+    CallArgumentError // Argument (return - CallArgumentError) caused an error
+};
 
-    Shiboken::GilState gil;
-    PyObject *pyArguments = isShortCuit
-        ? reinterpret_cast<PyObject *>(args[1]) : parseArguments(method, args);
-
-    if (pyArguments) {
-        QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
-        const char *returnType = method.typeName();
-        if (returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0) {
-            retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType));
-            if (!retConverter->isValid()) {
-                PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData());
-                return -1;
-            }
-        }
+static inline bool isNonVoidReturn(const char *returnType)
+{
+    return returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0;
+}
+
+static int callPythonMetaMethodHelper(const QByteArrayList &paramTypes,
+                                      const char *returnType /* = nullptr */,
+                                      void **args, PyObject *pyCallable)
+{
+    const qsizetype argsSize = paramTypes.size();
+    Shiboken::AutoDecRef preparedArgs(PyTuple_New(argsSize));
+
+    for (qsizetype i = 0; i < argsSize; ++i) {
+        void *data = args[i + 1];
+        auto param = paramTypes.at(i);
+        Shiboken::Conversions::SpecificConverter converter(param.constData());
+        if (!converter.isValid())
+            return CallResult::CallArgumentError + int(i);
+        PyTuple_SetItem(preparedArgs, i, converter.toPython(data));
+    }
+
+    QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
+    if (isNonVoidReturn(returnType)) {
+        retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType));
+        if (!retConverter->isValid())
+            return CallResult::CallReturnValueError;
+    }
 
-        Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments));
+    Shiboken::AutoDecRef retval(PyObject_CallObject(pyCallable, preparedArgs.object()));
+    if (PyErr_Occurred() != nullptr || retval.isNull())
+        return CallResult::CallOtherError;
 
-        if (!isShortCuit && pyArguments)
-            Py_DECREF(pyArguments);
+    if (retval != Py_None && !retConverter.isNull())
+        retConverter->toCpp(retval, args[0]);
+    return CallResult::CallOk;
+}
 
-        if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter)
-            retConverter->toCpp(retval, args[0]);
+int SignalManager::callPythonMetaMethod(QMetaMethod method, void **args,
+                                        PyObject *callable)
+{
+    Q_ASSERT(callable);
+
+    Shiboken::GilState gil;
+    int callResult = callPythonMetaMethodHelper(method.parameterTypes(),
+                                                method.typeName(), args, callable);
+    switch (callResult) {
+    case CallOk:
+        return 0;
+    case CallOtherError:
+        return -1;
+    case CallReturnValueError:
+        PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData());
+        return -1;
+    default: { // CallArgumentError + n
+        const int arg = callResult - CallArgumentError;
+        PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, arg).constData());
+        return -1;
     }
+    }
+    return 0;
+}
+
+static QByteArray signature(const char *name, const QByteArrayList &parameterTypes,
+                            const char *returnType)
+{
+    QByteArray result;
+    if (isNonVoidReturn(returnType))
+        result += QByteArray(returnType) + ' ';
+    result += QByteArray(name) + '(' + parameterTypes.join(", ") + ')';
+    return result;
+}
 
-    return -1;
+int SignalManager::callPythonMetaMethod(const QByteArrayList &parameterTypes,
+                                        const char *returnType,
+                                        void **args, PyObject *callable)
+{
+    Q_ASSERT(callable);
+
+    Shiboken::GilState gil;
+    int callResult = callPythonMetaMethodHelper(parameterTypes, returnType, args, callable);
+    switch (callResult) {
+    case CallOk:
+        return 0;
+    case CallOtherError:
+        return -1;
+    case CallReturnValueError: {
+        const auto &sig = signature("slot", parameterTypes, returnType);
+        PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(sig).constData());
+        return -1;
+    }
+    default: { // CallArgumentError + n
+        const int arg = callResult - CallArgumentError;
+        const auto &sig = signature("slot", parameterTypes, returnType);
+        const auto &msg = msgCannotConvertParameter(parameterTypes.at(arg), sig, arg);
+        PyErr_SetString(PyExc_TypeError, msg.constData());
+        return -1;
+    }
+    }
+    return 0;
 }
 
 bool SignalManager::registerMetaMethod(QObject *source, const char *signature, QMetaMethod::MethodType type)
@@ -741,47 +631,71 @@ QDebug operator<<(QDebug debug, const slotSignature &sig)
     return debug;
 }
 
-int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signature, QMetaMethod::MethodType type)
+static int addMetaMethod(QObject *source, const QByteArray &signature,
+                         QMetaMethod::MethodType type)
 {
-    if (!source) {
-        qWarning("SignalManager::registerMetaMethodGetIndex(\"%s\") called with source=nullptr.",
-                 signature);
+    const QMetaObject *metaObject = source->metaObject();
+    SbkObject *self = Shiboken::BindingManager::instance().retrieveWrapper(source);
+    if (!Shiboken::Object::hasCppWrapper(self)) {
+        qWarning().noquote().nospace() << __FUNCTION__
+            << ": Cannot add dynamic method \"" << signature << "\" (" << type
+            << ") to " << source << ": No Wrapper found.";
         return -1;
     }
-    const QMetaObject *metaObject = source->metaObject();
-    int methodIndex = metaObject->indexOfMethod(signature);
-    // Create the dynamic signal is needed
-    if (methodIndex == -1) {
-        SbkObject *self = Shiboken::BindingManager::instance().retrieveWrapper(source);
-        if (!Shiboken::Object::hasCppWrapper(self)) {
-            qWarning().noquote().nospace() << __FUNCTION__
-                << ": Cannot add dynamic method \"" << signature << "\" (" << type
-                << ") to " << source << ": No Wrapper found.";
-            return -1;
-        }
-        auto *pySelf = reinterpret_cast<PyObject *>(self);
-        auto *dict = SbkObject_GetDict_NoRef(pySelf);
-        MetaObjectBuilder *dmo = metaBuilderFromDict(dict);
-
-        // Create a instance meta object
-        if (!dmo) {
-            dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject);
-            PyObject *pyDmo = PyCapsule_New(dmo, nullptr, destroyMetaObject);
-            PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo);
-            Py_DECREF(pyDmo);
-        }
 
-        if (type == QMetaMethod::Slot) {
-            qCWarning(lcPySide).noquote().nospace()
-                << "Warning: Registering dynamic slot \""
-                << signature << "\" on \"" << source->metaObject()->className()
-                << "\". Consider annotating with " << slotSignature(signature);
-        }
+    auto *pySelf = reinterpret_cast<PyObject *>(self);
+    auto *dict = SbkObject_GetDict_NoRef(pySelf);
+    MetaObjectBuilder *dmo = metaBuilderFromDict(dict);
+    // Create a instance meta object
+    if (dmo == nullptr) {
+        dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject);
+        PyObject *pyDmo = PyCapsule_New(dmo, nullptr, destroyMetaObject);
+        PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo);
+        Py_DECREF(pyDmo);
+    }
+
+    if (type == QMetaMethod::Slot) {
+        qCWarning(lcPySide).noquote().nospace()
+            << "Warning: Registering dynamic slot \""
+            << signature << "\" on \"" << source->metaObject()->className()
+            << "\". Consider annotating with " << slotSignature(signature);
+    }
+
+    return type == QMetaMethod::Signal ? dmo->addSignal(signature) : dmo->addSlot(signature);
+}
+
+static inline void warnNullSource(const char *signature)
+{
+    qWarning("SignalManager::registerMetaMethodGetIndex(\"%s\") called with source=nullptr.",
+             signature);
+}
+
+int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signature,
+                                              QMetaMethod::MethodType type)
+{
+    if (source == nullptr) {
+        warnNullSource(signature);
+        return -1;
+    }
+    const QMetaObject *metaObject = source->metaObject();
+    const int methodIndex = metaObject->indexOfMethod(signature);
+    // Create the dynamic signal if needed
+    return methodIndex != -1
+        ? methodIndex : addMetaMethod(source, QByteArray(signature), type);
+}
 
-        return type == QMetaMethod::Signal
-            ? dmo->addSignal(signature) : dmo->addSlot(signature);
+int SignalManager::registerMetaMethodGetIndexBA(QObject* source, const QByteArray &signature,
+                                                QMetaMethod::MethodType type)
+{
+    if (source == nullptr) {
+        warnNullSource(signature.constData());
+        return -1;
     }
-    return methodIndex;
+    const QMetaObject *metaObject = source->metaObject();
+    const int methodIndex = metaObject->indexOfMethod(signature.constData());
+    // Create the dynamic signal if needed
+    return methodIndex != -1
+        ? methodIndex : addMetaMethod(source, signature, type);
 }
 
 const QMetaObject *SignalManager::retrieveMetaObject(PyObject *self)
@@ -803,32 +717,3 @@ const QMetaObject *SignalManager::retrieveMetaObject(PyObject *self)
 
     return builder->update();
 }
-
-static PyObject *parseArguments(const QMetaMethod &method, void **args)
-{
-    const auto &paramTypes = method.parameterTypes();
-    const qsizetype argsSize = paramTypes.size();
-    PyObject *preparedArgs = PyTuple_New(argsSize);
-
-    for (qsizetype i = 0; i < argsSize; ++i) {
-        void *data = args[i+1];
-        auto param = paramTypes.at(i);
-        Shiboken::Conversions::SpecificConverter converter(param.constData());
-        if (!converter) {
-            PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, i).constData());
-            Py_DECREF(preparedArgs);
-            return nullptr;
-        }
-        PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data));
-    }
-    return preparedArgs;
-}
-
-static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args)
-{
-    void *signalArgs[2] = {nullptr, args};
-    source->qt_metacall(QMetaObject::InvokeMetaMethod, signalIndex, signalArgs);
-    return true;
-}
-
-#include "signalmanager.moc"
index c531d663022baf7aca8ef4b447411f112b78575e..ee39a3a8dd060a0934ca12e254d6ecdcf705f5c5 100644 (file)
@@ -57,40 +57,30 @@ public:
 
     using QmlMetaCallErrorHandler = std::optional<int>(*)(QObject *object);
 
-    static SignalManager& instance();
+    static void init();
 
     static void setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler);
 
-    QObject* globalReceiver(QObject *sender, PyObject *callback, QObject *receiver = nullptr);
-    void releaseGlobalReceiver(const QObject* sender, QObject* receiver);
-    int globalReceiverSlotIndex(QObject* sender, const char* slotSignature) const;
-    void notifyGlobalReceiver(QObject* receiver);
-
-    bool emitSignal(QObject* source, const char* signal, PyObject* args);
+    static bool emitSignal(QObject* source, const char* signal, PyObject* args);
     static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args);
 
     // Used to register a new signal/slot on QMetaobject of source.
-    static bool registerMetaMethod(QObject* source, const char* signature, QMetaMethod::MethodType type);
-    static int registerMetaMethodGetIndex(QObject* source, const char* signature, QMetaMethod::MethodType type);
+    static bool registerMetaMethod(QObject* source, const char* signature,
+                                   QMetaMethod::MethodType type);
+    static int registerMetaMethodGetIndex(QObject* source, const char *signature,
+                                          QMetaMethod::MethodType type);
+    static int registerMetaMethodGetIndexBA(QObject* source, const QByteArray &signature,
+                                            QMetaMethod::MethodType type);
 
     // used to discovery metaobject
     static const QMetaObject* retrieveMetaObject(PyObject* self);
 
-    // Disconnect all signals managed by Globalreceiver
-    void clear();
-    void purgeEmptyGlobalReceivers();
-
-    // Utility function to call a python method usign args received in qt_metacall
-    static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj, bool isShortCuit);
-
-    static void deleteGlobalReceiver(const QObject *globalReceiver);
-
-private:
-    struct SignalManagerPrivate;
-    SignalManagerPrivate* m_d;
-
-    SignalManager();
-    ~SignalManager();
+    // Utility function to call a python method using args received in qt_metacall
+    static int callPythonMetaMethod(QMetaMethod method, void **args, PyObject *callable);
+    static int callPythonMetaMethod(const QByteArrayList &parameterTypes,
+                                    const char *returnType /* = nullptr */,
+                                    void **args, PyObject *callable);
+    static void handleMetaCallError();
 };
 
 }
index d484257e2613d90777de7bed639175ad4c818d6c..da0a2afd450e1a67baeccbb813613cc4bfb9e64d 100644 (file)
@@ -50,7 +50,7 @@ const char *PySideQmlAttachedPrivate::name() const
 
 extern "C" {
 
-static PyTypeObject *createPySideQmlAttachedType(void)
+static PyTypeObject *createPySideQmlAttachedType()
 {
     auto typeSlots =
         PySide::ClassDecorator::Methods<PySideQmlAttachedPrivate>::typeSlots();
@@ -92,7 +92,7 @@ static QObject *attachedFactoryHelper(PyTypeObject *attachingType, QObject *o)
 
     static const char methodName[] = "qmlAttachedProperties";
     static PyObject *const pyMethodName = Shiboken::String::createStaticString(methodName);
-    PyObject *attachingTypeObj = reinterpret_cast<PyObject *>(attachingType);
+    auto *attachingTypeObj = reinterpret_cast<PyObject *>(attachingType);
     Shiboken::AutoDecRef pyResult(PyObject_CallMethodObjArgs(attachingTypeObj, pyMethodName,
                                                              attachingTypeObj /* self */,
                                                              converter.toPython(&o),
@@ -178,7 +178,7 @@ PySide::Qml::QmlExtensionInfo qmlAttachedInfo(PyTypeObject *t,
     if (!info || info->attachedType == nullptr)
         return result;
 
-    auto *name = reinterpret_cast<PyTypeObject *>(t)->tp_name;
+    const auto *name = reinterpret_cast<PyTypeObject *>(t)->tp_name;
     if (nextAttachingType >= MAX_ATTACHING_TYPES) {
         qWarning("Unable to initialize attached type \"%s\": "
                  "The limit %d of  attached types has been reached.",
index 23543d589d8bceab79eead614b3b3d7e8db4be7b..9953c3623be3713912f90259aae3c0c706d67555 100644 (file)
@@ -46,7 +46,7 @@ const char *PySideQmlExtendedPrivate::name() const
 
 extern "C" {
 
-static PyTypeObject *createPySideQmlExtendedType(void)
+static PyTypeObject *createPySideQmlExtendedType()
 {
     auto typeSlots =
         PySide::ClassDecorator::Methods<PySideQmlExtendedPrivate>::typeSlots();
@@ -97,7 +97,7 @@ static QObject *extensionFactory(QObject *o)
     }
 
     Shiboken::AutoDecRef args(PyTuple_New(1));
-    PyTuple_SET_ITEM(args.object(), 0, pyObj);
+    PyTuple_SetItem(args.object(), 0, pyObj);
     auto *extensionTypeObj = reinterpret_cast<PyObject *>(info->extensionType);
     Shiboken::AutoDecRef pyResult(PyObject_Call(extensionTypeObj, args, nullptr));
     if (pyResult.isNull() || PyErr_Occurred()) {
index 18d39d121142c3c3ddf403171bf2c92d3759c254..90136f0aacb7cdf1216ce435c80019247f6b14e9 100644 (file)
@@ -35,7 +35,7 @@ PyObject *PySideQmlForeignPrivate::tp_call(PyObject *self, PyObject *args, PyObj
     info->foreignType = data->type();
     // Insert an alias to be used by the factory functions of Decorators like
     // @QmlExtended and @QmlAttached.
-    auto *foreignObj = reinterpret_cast<const PyObject *>(info->foreignType);
+    const auto *foreignObj = reinterpret_cast<const PyObject *>(info->foreignType);
     PySide::Qml::insertQmlTypeInfoAlias(foreignObj, info);
 
     Py_INCREF(klass);
@@ -49,7 +49,7 @@ const char *PySideQmlForeignPrivate::name() const
 
 extern "C" {
 
-static PyTypeObject *createPySideQmlForeignType(void)
+static PyTypeObject *createPySideQmlForeignType()
 {
     auto typeSlots =
         PySide::ClassDecorator::Methods<PySideQmlForeignPrivate>::typeSlots();
index 75bb5af9696e76b39fdad9a13304bf9960074e6a..447d1656400594bfe3aba9c283308ca7793112f9 100644 (file)
@@ -47,7 +47,7 @@ static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds)
                                    "designable", "scriptable", "stored",
                                    "user", "constant", "final",
                                    nullptr};
-    PySideProperty *pySelf = reinterpret_cast<PySideProperty *>(self);
+    auto *pySelf = reinterpret_cast<PySideProperty *>(self);
 
     auto *data = static_cast<QmlListPropertyPrivate *>(pySelf->d);
 
@@ -139,9 +139,9 @@ void propListAppender(QQmlListProperty<QObject> *propList, QObject *item)
 
     Shiboken::AutoDecRef args(PyTuple_New(2));
     PyTypeObject *qobjectType = qObjectType();
-    PyTuple_SET_ITEM(args, 0,
+    PyTuple_SetItem(args, 0,
                      Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
-    PyTuple_SET_ITEM(args, 1,
+    PyTuple_SetItem(args, 1,
                      Shiboken::Conversions::pointerToPython(qobjectType, item));
 
     auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
@@ -157,7 +157,7 @@ qsizetype propListCount(QQmlListProperty<QObject> *propList)
     Shiboken::GilState state;
 
     Shiboken::AutoDecRef args(PyTuple_New(1));
-    PyTuple_SET_ITEM(args, 0,
+    PyTuple_SetItem(args, 0,
                      Shiboken::Conversions::pointerToPython(qObjectType(), propList->object));
 
     auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
@@ -183,16 +183,16 @@ QObject *propListAt(QQmlListProperty<QObject> *propList, qsizetype index)
 
     Shiboken::AutoDecRef args(PyTuple_New(2));
     PyTypeObject *qobjectType = qObjectType();
-    PyTuple_SET_ITEM(args, 0,
+    PyTuple_SetItem(args, 0,
                      Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
     auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>();
-    PyTuple_SET_ITEM(args, 1,
+    PyTuple_SetItem(args, 1,
                      Shiboken::Conversions::copyToPython(converter, &index));
 
     auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
     Shiboken::AutoDecRef retVal(PyObject_CallObject(data->at, args));
 
-    QObject *result = 0;
+    QObject *result = nullptr;
     if (PyErr_Occurred())
         PyErr_Print();
     else if (PyType_IsSubtype(Py_TYPE(retVal), data->type))
@@ -207,7 +207,7 @@ void propListClear(QQmlListProperty<QObject> * propList)
 
     Shiboken::AutoDecRef args(PyTuple_New(1));
     PyTypeObject *qobjectType = qObjectType();
-    PyTuple_SET_ITEM(args, 0,
+    PyTuple_SetItem(args, 0,
                      Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
 
     auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
@@ -224,12 +224,12 @@ void propListReplace(QQmlListProperty<QObject> *propList, qsizetype index, QObje
 
     Shiboken::AutoDecRef args(PyTuple_New(3));
     PyTypeObject *qobjectType = qObjectType();
-    PyTuple_SET_ITEM(args, 0,
+    PyTuple_SetItem(args, 0,
                      Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
     auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>();
-    PyTuple_SET_ITEM(args, 1,
+    PyTuple_SetItem(args, 1,
                      Shiboken::Conversions::copyToPython(converter, &index));
-    PyTuple_SET_ITEM(args, 2,
+    PyTuple_SetItem(args, 2,
                      Shiboken::Conversions::pointerToPython(qobjectType, value));
 
     auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
@@ -246,7 +246,7 @@ void propListRemoveLast(QQmlListProperty<QObject> *propList)
 
     Shiboken::AutoDecRef args(PyTuple_New(1));
     PyTypeObject *qobjectType = qObjectType();
-    PyTuple_SET_ITEM(args, 0,
+    PyTuple_SetItem(args, 0,
                      Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
 
     auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
@@ -262,7 +262,7 @@ void QmlListPropertyPrivate::metaCall(PyObject *source, QMetaObject::Call call,
     if (call != QMetaObject::ReadProperty)
         return;
 
-    QObject *qobj;
+    QObject *qobj{};
     PyTypeObject *qobjectType = qObjectType();
     Shiboken::Conversions::pythonToCppPointer(qobjectType, source, &qobj);
     QQmlListProperty<QObject> declProp(
@@ -280,8 +280,11 @@ void QmlListPropertyPrivate::metaCall(PyObject *source, QMetaObject::Call call,
 }
 
 static const char *PropertyList_SignatureStrings[] = {
-    "PySide6.QtQml.ListProperty(self,type:type,append:typing.Callable,"
-        "at:typing.Callable=None,clear:typing.Callable=None,count:typing.Callable=None)",
+    "PySide6.QtQml.ListProperty(self,type:type,"
+        "append:typing.Optional[typing.Callable[...,typing.Any]]=None,"
+        "at:typing.Optional[typing.Callable[...,typing.Any]]=None,"
+        "clear:typing.Optional[typing.Callable[...,typing.Any]]=None,"
+        "count:typing.Optional[typing.Callable[...,typing.Any]]=None)",
     nullptr // Sentinel
 };
 
index 63cefedb5dfbf35eba729e351dc0caffd8ded3e2..7e7758114ef51f23bae0603c26c99e47dfc5c9ba 100644 (file)
@@ -40,7 +40,9 @@ std::optional<int> qmlMetaCallErrorHandler(QObject *object)
     if (engine->currentStackFrame == nullptr)
         return {};
 
-    PyObject *errType, *errValue, *errTraceback;
+    PyObject *errType{};
+    PyObject *errValue{};
+    PyObject *errTraceback{};
     PyErr_Fetch(&errType, &errValue, &errTraceback);
     // PYSIDE-464: The error is only valid before PyErr_Restore,
     // PYSIDE-464: therefore we take local copies.
@@ -53,10 +55,12 @@ std::optional<int> qmlMetaCallErrorHandler(QObject *object)
     PyErr_Print();    // Note: PyErr_Print clears the error.
 
     if (isSyntaxError)
-        return engine->throwSyntaxError(errString);
-    if (isTypeError)
-        return engine->throwTypeError(errString);
-    return engine->throwError(errString);
+        engine->throwSyntaxError(errString);
+    else if (isTypeError)
+        engine->throwTypeError(errString);
+    else
+        engine->throwError(errString);
+    return -1;
 #else
     Q_UNUSED(object);
     qWarning("libpyside6qml was built without QML private API support, error handling will not work.");
index 4ccd459d5a1f603a778038b6a8265839de9b686c..5fa53bb7a694ac58961ec64c12632c8dd9776e68 100644 (file)
@@ -43,7 +43,7 @@ static void createInto(void *memory, void *type)
     QMutexLocker locker(&PySide::nextQObjectMemoryAddrMutex());
     PySide::setNextQObjectMemoryAddr(memory);
     Shiboken::GilState state;
-    PyObject *obj = PyObject_CallObject(reinterpret_cast<PyObject *>(type), 0);
+    PyObject *obj = PyObject_CallObject(reinterpret_cast<PyObject *>(type), nullptr);
     if (!obj || PyErr_Occurred())
         PyErr_Print();
     PySide::setNextQObjectMemoryAddr(nullptr);
@@ -76,7 +76,7 @@ static PyTypeObject *qQJSValueType()
 // Check if o inherits from baseClass
 static bool inheritsFrom(const QMetaObject *o, const char *baseClass)
 {
-    for (auto *base = o->superClass(); base ; base = base->superClass()) {
+    for (const auto *base = o->superClass(); base ; base = base->superClass()) {
         if (qstrcmp(base->className(), baseClass) == 0)
             return true;
     }
@@ -171,7 +171,7 @@ static PyTypeObject *checkTypeObject(PyObject *pyObj, const char *what)
 static bool setClassInfo(PyTypeObject *type, const QByteArray &key, const QByteArray &value)
 {
     if (!PySide::ClassInfo::setClassInfo(type, key, value)) {
-        PyErr_Format(PyExc_TypeError, "Setting class info \"%s\" to \"%s\" on \"%s\" failed.",
+        PyErr_Format(PyExc_TypeError, R"(Setting class info "%s" to "%s" on "%s" failed.)",
                      key.constData(), value.constData(), type->tp_name);
         return false;
     }
@@ -197,7 +197,7 @@ static int qmlRegisterType(PyObject *pyObj,
                            const QMetaObject *metaObject,
                            const QMetaObject *classInfoMetaObject = nullptr)
 {
-    PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+    auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
 
     if (classInfoMetaObject == nullptr)
         classInfoMetaObject = metaObject;
@@ -270,7 +270,7 @@ static int qmlRegisterType(PyObject *pyObj,
 static int qmlRegisterType(PyObject *pyObj, PyObject *pyClassInfoObj,
                            const ImportData &importData)
 {
-    PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+    auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
     if (!isQObjectDerived(pyObjType, true))
         return -1;
 
@@ -438,7 +438,7 @@ public:
     {
         Shiboken::GilState gil;
         Shiboken::AutoDecRef args(PyTuple_New(1));
-        PyTuple_SET_ITEM(args, 0,
+        PyTuple_SetItem(args, 0,
                          Shiboken::Conversions::pointerToPython(qQmlEngineType(), engine));
         PyObject *retVal = PyObject_CallObject(data()->callable, args);
         QObject *result = handleReturnValue(retVal);
@@ -456,7 +456,7 @@ static int qmlRegisterSingletonTypeV2(PyObject *pyObj, PyObject *pyClassInfoObj,
                                       const ImportData &importData,
                                       const SingletonQObjectCreation &callback)
 {
-    PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+    auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
     if (!isQObjectDerived(pyObjType, true))
         return -1;
 
@@ -501,7 +501,7 @@ static int qmlRegisterSingletonType(PyObject *pyObj, const ImportData &importDat
     const QMetaObject *metaObject = nullptr;
 
     if (isQObject) {
-        PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+        auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
 
         if (!isQObjectDerived(pyObjType, true))
             return -1;
@@ -540,7 +540,7 @@ static int qmlRegisterSingletonType(PyObject *pyObj, const ImportData &importDat
                 Shiboken::GilState gil;
                 AutoDecRef args(PyTuple_New(1));
 
-                PyTuple_SET_ITEM(args, 0, Conversions::pointerToPython(
+                PyTuple_SetItem(args, 0, Conversions::pointerToPython(
                                  qQmlEngineType(), engine));
 
                 AutoDecRef retVal(PyObject_CallObject(callback, args));
@@ -583,7 +583,7 @@ static int qmlRegisterSingletonInstance(PyObject *pyObj, const ImportData &impor
     using namespace Shiboken;
 
     // Check if the Python Type inherit from QObject
-    PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+    auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
 
     if (!isQObjectDerived(pyObjType, true))
         return -1;
@@ -698,7 +698,7 @@ PyObject *qmlElementMacro(PyObject *pyObj, const char *decoratorName,
     const auto importDataO = getGlobalImportData(decoratorName);
     if (!importDataO.has_value())
         return nullptr;
-    const auto importData = importDataO.value();
+    const auto &importData = importDataO.value();
 
     int result{};
     if (mode == RegisterMode::Singleton) {
index 7c0f6b8ff82c223ff7831e4f62b9a0b40e21b821..938b6a85882d6e90a60b95a1d0a4728d662b9afd 100644 (file)
@@ -50,7 +50,7 @@ int PySideQmlUncreatablePrivate::tp_init(PyObject *self, PyObject *args, PyObjec
     if (argsCount == 0) {
         result = 0; // QML-generated reason
     } else if (argsCount == 1) {
-        PyObject *arg = PyTuple_GET_ITEM(args, 0);
+        PyObject *arg = PyTuple_GetItem(args, 0);
         result = arg == Py_None
             ? 0 // QML-generated reason
             : convertToString(self, args);
index 7176523141aea33bba069f3654d522ed2f953e4e..4e2e4ad0401e019b45790a6e4a392f159654d82f 100644 (file)
@@ -23,7 +23,7 @@ target_sources(PySidePlugin PRIVATE
 target_compile_definitions(PySidePlugin PRIVATE -DQT_NO_KEYWORDS=1)
 
 if(PYTHON_LIMITED_API)
-    target_compile_definitions(PySidePlugin PRIVATE "-DPy_LIMITED_API=0x03050000")
+    target_compile_definitions(PySidePlugin PRIVATE "-DPy_LIMITED_API=0x03080000")
 endif()
 
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
index d23156a9d782a0e55bbf4e40f98d5188d9b6f727..dc8bdf435321bbeafc89c5f0b8b0f6b4a75b1e33 100644 (file)
@@ -17,6 +17,7 @@
 #include <QtCore/QVariant>
 
 #include <string_view>
+#include <utility>
 
 using namespace Qt::StringLiterals;
 
@@ -123,26 +124,29 @@ static bool runPyScriptFile(const QString &fileName, QString *errorMessage)
     return ok;
 }
 
+static std::pair<int, int> pythonVersion()
+{
+    // read environment set by pyside_tool.py
+    bool majorOk{};
+    bool minorOk{};
+    const int majorVersion = qEnvironmentVariableIntValue("PY_MAJOR_VERSION", &majorOk);
+    const int minorVersion = qEnvironmentVariableIntValue("PY_MINOR_VERSION", &minorOk);
+    if (majorOk && minorVersion)
+        return {majorVersion, minorVersion};
+    return {PY_MAJOR_VERSION, PY_MINOR_VERSION};
+}
+
 static void initVirtualEnvironment()
 {
     static const char virtualEnvVar[] = "VIRTUAL_ENV";
-    // As of Python 3.8/Windows, Python is no longer able to run stand-alone in
-    // a virtualenv due to missing libraries. Add the path to the modules
-    // instead. macOS seems to be showing the same issues.
+    // Since Python 3.8 (Windows, macOS), Python is no longer able to run stand
+    // -alone in a virtualenv due to missing libraries. Add the path to the modules
+    // instead.
 
     const auto os = QOperatingSystemVersion::currentType();
 
-    bool ok;
-    int majorVersion = qEnvironmentVariableIntValue("PY_MAJOR_VERSION", &ok);
-    int minorVersion = qEnvironmentVariableIntValue("PY_MINOR_VERSION", &ok);
-    if (!ok) {
-        majorVersion = PY_MAJOR_VERSION;
-        minorVersion = PY_MINOR_VERSION;
-    }
-
     if (!qEnvironmentVariableIsSet(virtualEnvVar)
-        || (os != QOperatingSystemVersion::MacOS && os != QOperatingSystemVersion::Windows)
-        || (majorVersion == 3 && minorVersion < 8)) {
+        || (os != QOperatingSystemVersion::MacOS && os != QOperatingSystemVersion::Windows)) {
         return;
     }
 
@@ -155,11 +159,13 @@ static void initVirtualEnvironment()
     case QOperatingSystemVersion::Windows:
         pythonPath.append(virtualEnvPath + R"(\Lib\site-packages)");
         break;
-    case QOperatingSystemVersion::MacOS:
+    case QOperatingSystemVersion::MacOS: {
+        const auto version = pythonVersion();
         pythonPath.append(virtualEnvPath + "/lib/python"_ba +
-                          QByteArray::number(majorVersion) + '.'
-                          + QByteArray::number(minorVersion)
+                          QByteArray::number(version.first) + '.'
+                          + QByteArray::number(version.second)
                           + "/site-packages"_ba);
+    }
         break;
     default:
         break;
index 976754febd44ff7dda0b1a2a5d6667602b06f155..8142ed5fd4d15d75e5e80cb295567143902b5376 100644 (file)
@@ -79,7 +79,7 @@ QWidget *PyCustomWidget::createWidget(QWidget *parent)
     }
 
     Shiboken::AutoDecRef pyArgs(PyTuple_New(1));
-    PyTuple_SET_ITEM(pyArgs.object(), 0, pyParent); // tuple will keep pyParent reference
+    PyTuple_SetItem(pyArgs.object(), 0, pyParent); // tuple will keep pyParent reference
 
     // Call python constructor
     auto *result = reinterpret_cast<SbkObject *>(PyObject_CallObject(m_pyObject, pyArgs));
index bd78d331681d78e5f17a1af4283fc213a0b47ab2..830b31e91015a62a2a99b5238c0fd684fc47256e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 major_version = "@pyside_MAJOR_VERSION@"
 minor_version = "@pyside_MINOR_VERSION@"
index 1562f7b27c9916dbf1560c58fa1ff00060bab584..250b2c292914a7ea19ae31bbefebbd0ca65e60f7 100644 (file)
@@ -18,7 +18,7 @@ add_library(QtExampleIcons MODULE module.c)
 
 # See libshiboken/CMakeLists.txt
 if(PYTHON_LIMITED_API)
-    target_compile_definitions(QtExampleIcons PRIVATE "-DPy_LIMITED_API=0x03050000")
+    target_compile_definitions(QtExampleIcons PRIVATE "-DPy_LIMITED_API=0x03080000")
 endif()
 
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
index 814204f1aaef657c1220a03cb348cc9a51818d80..b728dafe6c53d6cd226c38fc4738238cbad0e2bf 100644 (file)
@@ -33,7 +33,6 @@ MODULE_EXPORT PyObject *PyInit_QtExampleIcons(void)
 int main(int argc, char *argv[])
 {
 #ifndef PYPY_VERSION
-    Py_SetProgramName(L"module-test");
     Py_Initialize();
 #endif
     PyInit_QtExampleIcons();
index 23e2d6e7e7ee0c4be9cd2840875f1a0b872fc78a..a748097c3c91efae2fea491b0e1cc7a815c24b65 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Q3DExtras'''
 
@@ -15,11 +16,10 @@ init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
 
-from PySide6.QtCore import (Property, QObject, QPropertyAnimation, QTimer, Signal, Slot)
-from PySide6.QtGui import (QGuiApplication, QMatrix4x4, QQuaternion, QVector3D, QWindow)
-from PySide6.Qt3DCore import (Qt3DCore)
-from PySide6.Qt3DRender import (Qt3DRender)
-from PySide6.Qt3DExtras import (Qt3DExtras)
+from PySide6.QtCore import Property, QObject, QTimer, Signal
+from PySide6.QtGui import QMatrix4x4, QQuaternion, QVector3D
+from PySide6.Qt3DCore import Qt3DCore
+from PySide6.Qt3DExtras import Qt3DExtras
 
 
 class OrbitTransformController(QObject):
diff --git a/sources/pyside6/tests/QtAsyncio/bug_2790.py b/sources/pyside6/tests/QtAsyncio/bug_2790.py
new file mode 100644 (file)
index 0000000..9fd152b
--- /dev/null
@@ -0,0 +1,47 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+'''Test cases for QtAsyncio'''
+
+import unittest
+import asyncio
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+class QAsyncioTestCaseBug2790(unittest.TestCase):
+
+    async def producer(self, products: list[str]):
+        while True:
+            products.append("product")
+            await asyncio.sleep(2)
+
+    async def task(self, outputs: list[str]):
+        products = []
+        asyncio.ensure_future(self.producer(products))
+        for _ in range(6):
+            try:
+                async with asyncio.timeout(0.5):
+                    while len(products) == 0:
+                        await asyncio.sleep(0)
+                    outputs.append(products.pop(0))
+            except TimeoutError:
+                outputs.append("Timeout")
+
+    def test_timeout(self):
+        # The Qt event loop (and thus QtAsyncio) does not guarantee that events
+        # will be processed in the order they were posted, so there is two
+        # possible outputs for this test.
+        outputs_expected_1 = ["product", "Timeout", "Timeout", "Timeout", "Timeout", "product"]
+        outputs_expected_2 = ["product", "Timeout", "Timeout", "Timeout", "product", "Timeout"]
+
+        outputs_real = []
+
+        QtAsyncio.run(self.task(outputs_real), keep_running=False)
+
+        self.assertTrue(outputs_real in [outputs_expected_1, outputs_expected_2])
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/sources/pyside6/tests/QtAsyncio/bug_2799.py b/sources/pyside6/tests/QtAsyncio/bug_2799.py
new file mode 100644 (file)
index 0000000..eb86c7d
--- /dev/null
@@ -0,0 +1,30 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+"""Test cases for QtAsyncio"""
+
+import unittest
+import asyncio
+import sys
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+@unittest.skipIf(sys.version_info < (3, 11), "Requires ExceptionGroup")
+class QAsyncioTestCaseBug2799(unittest.TestCase):
+    async def job(self):
+        await asyncio.sleep(1)
+
+    async def main(self):
+        async with asyncio.TaskGroup() as tg:
+            tg.create_task(self.job())
+            raise RuntimeError()
+
+    def test_exception_group(self):
+        with self.assertRaises(ExceptionGroup):
+            QtAsyncio.run(self.main(), keep_running=False)
+
+
+if __name__ == "__main__":
+    unittest.main()
index f3c971285af56385359b971afe9ccff966c6d33d..80c5107acd7b5f8d3240511625ad9c8e716679cb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
index 7ef2bb90d5f915fc876dbdb0ca93c98ae02c8adf..c2f56d01aaf7042586e0cccd306132ae7ecb2b6d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
index aa8ce4718bdb2b3619f862771bb6f7b9e39cecc6..f3724f0101a09256cd46a0f9b7afe233a7075493 100644 (file)
@@ -1,15 +1,28 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
 import asyncio
 import unittest
+import sys
 
 import PySide6.QtAsyncio as QtAsyncio
 
 
+@unittest.skipIf(sys.version_info < (3, 11), "Requires ExceptionGroup")
 class QAsyncioTestCaseCancelTaskGroup(unittest.TestCase):
+
+    """
+    PYSIDE-2644: If a task was cancelled, then a new future created from this
+    task should be cancelled as well. Otherwise, in some scenarios like a loop
+    inside the task and with bad timing, if the new future is not cancelled,
+    the task would continue running in this loop despite having been cancelled.
+    This bad timing can occur especially if the first future finishes very
+    quickly.
+    """
+
     def setUp(self) -> None:
         super().setUp()
         # We only reach the end of the loop if the task is not cancelled.
@@ -47,7 +60,7 @@ class QAsyncioTestCaseCancelTaskGroup(unittest.TestCase):
         for coro in coros:
             try:
                 QtAsyncio.run(self.main(coro), keep_running=False)
-            except ExceptionGroup as e:
+            except ExceptionGroup as e:  # noqa: F821
                 self.assertEqual(len(e.exceptions), 1)
                 self.assertIsInstance(e.exceptions[0], RuntimeError)
                 self.assertFalse(self._loop_end_reached)
index a0a949720e5c7d95affd5565d9bf4870f5fcc28b..a8e5bb1983f860a89465474f758bbe466f88bf1c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
index 25e680b3979e3eba87074c60382c40290806b66f..641d374df04603f97b29019de4e250b6de71facc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
index 0bd98c361f285811e7ec2e12816b1fedc4aad579..2df39ee02c798f0f51c18869056dd13f040a19a5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
index 5b52db239df1b409c10278bf55b51fa64cff7656..4893543498536f388577da139e71ac1d285a4c7d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
index 07a1266444d6523b202dd66650b7e020304bb4d7..66f0433dfd439ac9050cfbc223d80907ab81b607 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtAsyncio'''
 
diff --git a/sources/pyside6/tests/QtAsyncio/qasyncio_test_uncancel.py b/sources/pyside6/tests/QtAsyncio/qasyncio_test_uncancel.py
new file mode 100644 (file)
index 0000000..0366228
--- /dev/null
@@ -0,0 +1,61 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+"""Test cases for QtAsyncio"""
+
+import unittest
+import asyncio
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+class QAsyncioTestCaseUncancel(unittest.TestCase):
+    """ https://superfastpython.com/asyncio-cancel-task-cancellation """
+
+    async def worker(self, outputs: list[str]):
+        # Ensure the task always gets done.
+        while True:
+            try:
+                await asyncio.sleep(2)
+                outputs.append("Task sleep completed normally")
+                break
+            except asyncio.CancelledError:
+                outputs.append("Task is cancelled, ignore and try again")
+                asyncio.current_task().uncancel()
+
+    async def main(self, outputs: list[str]):
+        task = asyncio.create_task(self.worker(outputs))
+        # Allow the task to run briefly.
+        await asyncio.sleep(0.5)
+        task.cancel()
+        try:
+            await task
+        except asyncio.CancelledError:
+            outputs.append("Task was cancelled")
+
+        cancelling = task.cancelling()
+        self.assertEqual(cancelling, 0)
+        outputs.append(f"Task cancelling: {cancelling}")
+
+        cancelled = task.cancelled()
+        self.assertFalse(cancelled)
+        outputs.append(f"Task cancelled: {cancelled}")
+
+        done = task.done()
+        self.assertTrue(done)
+        outputs.append(f"Task done: {done}")
+
+    def test_uncancel(self):
+        outputs_expected = []
+        outputs_real = []
+
+        asyncio.run(self.main(outputs_real))
+        QtAsyncio.run(self.main(outputs_expected), keep_running=False)
+
+        self.assertIsNotNone(outputs_real)
+        self.assertEqual(outputs_real, outputs_expected)
+
+
+if __name__ == "__main__":
+    unittest.main()
index 21801106f19ce3fd8e7bed59d2d7354e0d8ed18d..0627333e698670dd503be60151b484c2f4d18605 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QBluetoothLocalDevice'''
 
index 0f7298fec37cd3691b281c8dc511a8aad83f0f3e..2bc623b7e0fa719e0737ea55c10b7f809ee97c9a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QLowEnergyServiceData'''
 
index 6f2bd66f2ab89742a626f15415d2d45755b7d82d..a0e12e0c4310d791f3665bd9925645f484aae429 100644 (file)
@@ -1,3 +1,4 @@
 {
-    "files": ["qcharts_test.py"]
+    "files": ["qcharts_test.py",
+              "qcharts_numpy_test.py"]
 }
index 8154020c0d15c7023c9bee750c7690244f7d9d62..4d05d0944b3452fcfd2d70270dba9c30241edadb 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QCharts/numpy'''
 
@@ -19,7 +20,6 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import QCoreApplication
 from PySide6.QtCharts import QLineSeries
 
 
index 8d57c07eb45b8320b12b08fc6983963fb619ee64..9c556c6c8d8362eb7e4ee9e3416b03d62f50a483 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QCharts'''
 
@@ -14,8 +15,8 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import QRect, QSize, QTimer
-from PySide6.QtGui import QGuiApplication, QScreen
+from PySide6.QtCore import QTimer
+from PySide6.QtGui import QGuiApplication
 from PySide6.QtCharts import QChart, QChartView, QPieSeries
 
 
index c82dbbea35ddda73886f220b36148e85003bf627..ca66e0e17ae503912cf3e0d171ae5654eb8e631b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 '''
 Unit tests for attribute cache in Python 3
index 493abb071d5c7f7258a7532e5fe5d3c31e894797..0f8425095d1699426921c0ef3426760764f27f06 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test case for QObject.signalsBlocked() and blockSignal()'''
 
@@ -138,14 +139,14 @@ class TestQFileSignalBlocking(unittest.TestCase):
 
         self.qfile.aboutToClose.connect(self.callback)
 
-        self.assertTrue(self.qfile.open(QFile.ReadOnly))
+        self.assertTrue(self.qfile.open(QFile.OpenModeFlag.ReadOnly))
         self.qfile.close()
         self.assertTrue(self.called)
 
         self.called = False
         self.qfile.blockSignals(True)
 
-        self.assertTrue(self.qfile.open(QFile.ReadOnly))
+        self.assertTrue(self.qfile.open(QFile.OpenModeFlag.ReadOnly))
         self.qfile.close()
         self.assertTrue(not self.called)
 
index 4a35956b16885717ccf2383bbb54c066c312c758..0257dda94e8de618c1be2538b27b019878378966 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 88c4bb51e2a532ab6c2462987b0fc24895fad212..fb8627e23e66072b00fee61dfdcdeaef6a6579c3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' unit test for BUG #1063 '''
 
@@ -21,7 +22,7 @@ class QTextStreamTestCase(unittest.TestCase):
         self.temp_file = tempfile.NamedTemporaryFile(delete=False)
         self.temp_file.close()
         self.f = QFile(self.temp_file.name)
-        self.f.open(QIODevice.WriteOnly)
+        self.f.open(QIODevice.OpenModeFlag.WriteOnly)
         self.strings = ('foo', 'bar')
         self.stream = QTextStream(self.f)
 
index 760918cd974f6c86f3c76a2127bb32e18c89094c..85766225d34bb93f11b89d11cec78251e831501f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' unit test for BUG #1069 '''
 
@@ -18,7 +19,7 @@ from PySide6.QtCore import QByteArray, QDataStream, QIODevice
 class QDataStreamOpOverloadTestCase(unittest.TestCase):
     def setUp(self):
         self.ba = QByteArray()
-        self.stream = QDataStream(self.ba, QIODevice.WriteOnly)
+        self.stream = QDataStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
 
     def testIt(self):
         self.stream << "hello"
index ecc22c43b2c66425651f8a3dce33b20e6024c0aa..3d66a6d2c700562dc09020d66bfb91e10c15cfe1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' unit test for BUG #1313 '''
 
index 8d2f4b47e503371d12c1df100f02b02556dd942b..fcd8dcec3b2af8a1dd74e2b3e196313dac0fb978 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -30,5 +31,3 @@ class QQtVersionTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
-
index b9768bf9dd91d0174b9a2bc0ec4bdf4d5891ef12..1862dc56ae62e0a701f9992014e2480ea955b481 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 0aa17c465732ea509e277beede3b1b93c5c67f97..a93f9408983d18d719b80fb2fe2c31227251b4c7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 324: http://bugs.openbossa.org/show_bug.cgi?id=324'''
 
index 8d55bc87ff8a78057e01f79d9fde432d5312a7c4..9e910de1fb11932a2c3e02ece5ffe3d71c369294 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 30459172c513a4318ca04b884ee5b9d32da12cb9..2fb886e57fa237cca0ee9fc846dfd681a0fb2e13 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -30,7 +31,7 @@ class QIODeviceTest(unittest.TestCase):
 
     def testIt(self):
         device = MyDevice("hello world\nhello again")
-        device.open(QIODevice.ReadOnly)
+        device.open(QIODevice.OpenModeFlag.ReadOnly)
 
         s = QTextStream(device)
         self.assertEqual(s.readLine(), "hello world")
index 8fb5d4827acb941e57fd194e13bbea531a96cd0c..11617aaf78e72872d4557552a8dd031042ebdb1a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 04a42fe08e756d40e81b0c3f9569e10f6f5f2ba7..c10eb5593f30e353e17c78a4b4ff9aacd2645d19 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -15,8 +16,8 @@ from PySide6.QtCore import QObject, QCoreApplication, QEvent, QThread
 
 class MyEvent(QEvent):
     def __init__(self, i):
-        print("TYPE:", type(QEvent.User))
-        super().__init__(QEvent.Type(QEvent.User))
+        print("TYPE:", type(QEvent.Type.User))
+        super().__init__(QEvent.Type(QEvent.Type.User))
         self.i = i
 
 
index e8655a84e0f02b1a5bcfbd985c5c4f60cd7ea34f..8c4a045df3dd110381f9cbae707a46075a76f4b8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 4e270a441a156131e90469f90be21d31b495c686..f63e86c95681b007be1d009e5d8815499102bb95 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """ Unittest for bug #515 """
 """ http://bugs.openbossa.org/show_bug.cgi?id=515 """
index 4fed16b6ee5eec4b44b33b518732cc024f28a1ee..58a78bd469d1c19c2d9c822d5a0ced7e10442df2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,6 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-import PySide6
 from PySide6.QtCore import QPoint, QPointF
 from PySide6.QtCore import QLine, QLineF
 from PySide6.QtCore import QSize, QSizeF
@@ -26,12 +26,12 @@ class testCases(unittest.TestCase):
         self.assertEqual((1, 2), p.toTuple())
 
     def testQLineToTuple(self):
-        l = QLine(1, 2, 3, 4)
-        self.assertEqual((1, 2, 3, 4), l.toTuple())
+        line = QLine(1, 2, 3, 4)
+        self.assertEqual((1, 2, 3, 4), line.toTuple())
 
     def testQLineFToTuple(self):
-        l = QLineF(1, 2, 3, 4)
-        self.assertEqual((1, 2, 3, 4), l.toTuple())
+        line = QLineF(1, 2, 3, 4)
+        self.assertEqual((1, 2, 3, 4), line.toTuple())
 
     def testQSizeToTuple(self):
         s = QSize(1, 2)
index 066dc1a0238759b44a931003819903ada8e3d354..75131326c2ffee49777a0240308bf6e6ac8b7b62 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 6425a4b86990bceee11d76f224af3d8757300e0e..808e42b72d85eec95dc580ea2b73e8871fa3b400 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 02da46ff1abb45e8601ba3411841e1418ad52109..dbd686cdd9631e6edd58f758e2b1ff3aabda0671 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 7db773240b113ff60c28f8e4c288395aa6dad2d8..1fa04f77f0aa29ffe62477532c188813ca22f554 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index ba0d330682b638fe221fbfed3ddb3bda9ce5af5e..10a0750fa9d791e0dc2cccf307054e7690a47e28 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import functools
 import os
@@ -11,7 +12,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject, Slot, Signal, SIGNAL
+from PySide6.QtCore import QObject, Slot, Signal
 
 
 def log_exception():
@@ -79,4 +80,3 @@ class SlotWithDecoratorTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 95edc0a7c14813c22e3ebca6db2bea33734ed10b..7ac0fd8955e7427e31b09d258b105680ca040c31 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,8 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QEvent, Qt
-import PySide6
+from PySide6.QtCore import QEvent
 
 
 TEST_EVENT_TYPE = QEvent.Type(QEvent.registerEventType())
@@ -30,8 +30,8 @@ class TestEvent(QEvent):
 
 class TestEnums(unittest.TestCase):
     def testUserTypesValues(self):
-        self.assertTrue(QEvent.User <= TestEvent.TestEventType <= QEvent.MaxUser)
-        self.assertTrue(QEvent.User <= TEST_EVENT_TYPE <= QEvent.MaxUser)
+        self.assertTrue(QEvent.Type.User <= TestEvent.TestEventType <= QEvent.Type.MaxUser)
+        self.assertTrue(QEvent.Type.User <= TEST_EVENT_TYPE <= QEvent.Type.MaxUser)
 
 
 if __name__ == '__main__':
index 9a57e0399c3ee9239636253be0ee4f688dac0364..9158ab35b15c49468f54a270cfd545c1aee07e48 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # Copyright (C) 2011 Thomas Perl <thp.io/about>
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # Test case for PySide bug 829
 
@@ -30,19 +31,19 @@ class QVariantConversions(unittest.TestCase):
         del confFile
         # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
         gc.collect()
-        s = QSettings(self._confFileName, QSettings.IniFormat)
-        self.assertEqual(s.status(), QSettings.NoError)
+        s = QSettings(self._confFileName, QSettings.Format.IniFormat)
+        self.assertEqual(s.status(), QSettings.Status.NoError)
         # Save value
         s.setValue('x', {1: 'a'})
         s.sync()
-        self.assertEqual(s.status(), QSettings.NoError)
+        self.assertEqual(s.status(), QSettings.Status.NoError)
         del s
         # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
         gc.collect()
 
         # Restore value
-        s = QSettings(self._confFileName, QSettings.IniFormat)
-        self.assertEqual(s.status(), QSettings.NoError)
+        s = QSettings(self._confFileName, QSettings.Format.IniFormat)
+        self.assertEqual(s.status(), QSettings.Status.NoError)
         self.assertEqual(s.value('x'), {1: 'a'})
 
     def __del__(self):
index 6d91b6e0a8f821d1e3d8a0e534e45a890a4e66ea..6bf4f261e5c8760e030676aafa9bcc652c94c70a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,7 +18,7 @@ get_counter = 0
 set_counter = 0
 
 
-class Descriptor(object):
+class Descriptor:
     def __get__(self, obj, owner):
         global get_counter
 
index 23c8d6201bff1e07e4d1ce9fc893cb9e5c29097a..d39c44547e7df798fb5fbf640727553b6062f2c9 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index c15a7014b4b48c6c53082c89cddc459247015b87..fb1ffdde1e2f7e3fbfec37368dcacd39919960d1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f233c2a26a23e6cc8b413ada8af1986b47c606d8..f710e1fbb0a862c204cb7a771eb7012e367f9032 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 3aca25947de3350493f41ddd7f27301b0ff6070d..90df0cf535b5a1571813a58fc2e74ec80706baa4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,7 +18,7 @@ class TestBug938 (unittest.TestCase):
 
     def testIt(self):
         b = QBuffer()
-        b.open(QBuffer.WriteOnly)
+        b.open(QBuffer.OpenModeFlag.WriteOnly)
         b.write(bytes("\x0023\x005", "UTF-8"))
         b.close()
         self.assertEqual(b.buffer().size(), 5)
index a9cacc97e50d79cbef61da12de0167152c2cf5ac..b772b8536e1380160f2e7ce0cd47efdc9de2c414 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index a8c9799e0407df80fa283a0454f30b67fd5d9dd7..82494fe4b280bcce3f0e14563d2dbac5ec301a36 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 464fe31207e55e7b92120c0ea0a0cde71094215a..5f76e5620d9a9cffaf95d93d7a42fed619e702a6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -28,7 +29,7 @@ class TestBug944 (unittest.TestCase):
 
     def testIt(self):
         device = MyIODevice()
-        device.open(QIODevice.ReadOnly)
+        device.open(QIODevice.OpenModeFlag.ReadOnly)
         s = QTextStream(device)
         self.assertEqual(s.read(4), "\0a\0a")
         self.assertEqual(device.readLine(), "\0b\0b\0b\0b")
index 84859af84469bdcefde66982df157ad100979721..fed0f9fdf57ba9946c01e5cf0b43bd258cc3576e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -43,7 +44,7 @@ class TestBugPYSIDE164(unittest.TestCase):
         eventloop = QEventLoop()
         emitter = Emitter()
         receiver = Receiver(eventloop)
-        emitter.signal.connect(receiver.receive, Qt.BlockingQueuedConnection)
+        emitter.signal.connect(receiver.receive, Qt.ConnectionType.BlockingQueuedConnection)
         emitter.start()
         retval = eventloop.exec()
         emitter.wait(2000)
index 3d6c603b7ec4816782fcb4324fea76b211c0245a..99f2e498f91a6cb2b06f37fc6e22c5c9371eb23d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 2830f8050ae44a91b266c3912cff65925f1c4949..02a43d83a601732b9162dc8c36fa909ea0e1d676 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b602b756959162c3a0cf5eb6cdaa5d0fa3272578..1572e6de33ba60d62f7c0c74bdb4a91277700e21 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 6b17ddc937bc6586b7a5a65f0b1ebeff2855d0ac..a0b9f1c73cdbbe6f0214c44a7478ca98fc002453 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for QObject.childEvent and QTimer.childEvent overloading'''
 
@@ -13,7 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject, QTimer, QCoreApplication
+from PySide6.QtCore import QObject, QTimer
 from helper.usesqapplication import UsesQApplication
 
 
@@ -61,4 +62,3 @@ class TestChildEvent(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 0b0a0b4b6b658d912756ed4e446ca521de181162..03f60db2257225c4a7c212f2735a5c244de2ece9 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
-import sys
 import os
 import sys
 import unittest
@@ -93,7 +93,7 @@ class TestClassInfo(unittest.TestCase):
             pass
         self.assertRaises(TypeError, make_info(), test_function)
 
-        class NotAQObject(object):
+        class NotAQObject:
             pass
         self.assertRaises(TypeError, make_info(), NotAQObject)
 
index e130628e37819b0858f2ccc5a13d29ce37f438f2..8b211a979f9ee72eb52bead2b1783be4e3ec5e94 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 from copy import deepcopy
 import os
@@ -44,7 +45,7 @@ class QTimeDeepCopy(DeepCopyHelper, unittest.TestCase):
 
 class QDateTimeDeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
-        self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.LocalTime)
+        self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.TimeSpec.LocalTime)
 
 
 class QSizeDeepCopy(DeepCopyHelper, unittest.TestCase):
index 0ee078428285686cbe7fcd108e709f5478f20561..439192ba3bce61649c14b8cdb5936615f185e61b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QTimer.singleShot'''
 
@@ -14,7 +15,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject, QTimer, QCoreApplication, SIGNAL
+from PySide6.QtCore import QObject, QTimer
 from helper.usesqapplication import UsesQApplication
 
 
@@ -33,4 +34,3 @@ class TestDeleteLater(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index ad087b48961c591baf08d7f5a91aa6f218b7ede5..0384821f429f131638735b9021cd12eab1c60b74 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -75,4 +76,3 @@ class TestDestroyNoConnect(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 145863c3eb00961e8f21a267d9859b5cbf135451..2e5b5151b0923d9ce83863e997f4996cfe7ec478 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for duck punching new implementations of C++ virtual methods into object instances.'''
 
@@ -32,7 +33,8 @@ class Duck(QObject):
 
 
 class TestDuckPunchingOnQObjectInstance(UsesQApplication):
-    '''Test case for duck punching new implementations of C++ virtual methods into object instances.'''
+    '''Test case for duck punching new implementations of C++ virtual methods into
+       object instances.'''
 
     def setUp(self):
         # Acquire resources
@@ -64,7 +66,8 @@ class TestDuckPunchingOnQObjectInstance(UsesQApplication):
         parent.childEvent = None
 
     def testChildEventMonkeyPatchWithInheritance(self):
-        # Test if the new childEvent injected on a QObject's extension class instance is called from C++
+        '''Test if the new childEvent injected on a QObject's extension class
+           instance is called from C++'''
         parent = Duck()
 
         def childEvent(obj, event):
@@ -86,4 +89,3 @@ class TestDuckPunchingOnQObjectInstance(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 78dee1893f47d8cd9b95978b603be8bbe76d5a35..e8d4864ddd93a16556ee9c1631c5d947c2d503dd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 emoji-string-test.py
@@ -40,10 +41,7 @@ class TestStuff(QObject):
         self.testsig.emit(emoji_str)
 
     def plausi(self):
-        # Python 2 may be built with UCS-2 or UCS-4 support.
-        # UCS-2 creates 2 surrogate code points. See
-        # https://stackoverflow.com/questions/30775689/python-length-of-unicode-string-confusion
-        assert len(emoji_str) == 2 if sys.maxunicode > 0xffff else 3
+        assert len(emoji_str) == 2
 
 
 if __name__ == "__main__":
index 97c4f942e89cae81d341310085e3bdcbcb197348..0e8d759a102ddd92d314101029869ef4ae182b31 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import sys
@@ -7,7 +8,7 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
 from PySide6 import QtCore
@@ -15,7 +16,7 @@ from PySide6.QtWidgets import QApplication, QLabel
 
 is_pypy = hasattr(sys, "pypy_version_info")
 if not is_pypy:
-    from PySide6.support import feature
+    from PySide6.support import feature  # noqa: E402
 
 import inspect
 
@@ -36,12 +37,12 @@ class ErrormessagesWithFeatures(unittest.TestCase):
     probe = "called with wrong argument types"
 
     def setUp(self):
-        qApp or QApplication()
+        qApp or QApplication()  # noqa
         feature.reset()
 
     def tearDown(self):
         feature.reset()
-        qApp.shutdown()
+        qApp.shutdown()  # noqa
 
     def testCorrectErrorMessagesPlain(self):
         with self.assertRaises(TypeError) as cm:
@@ -50,40 +51,40 @@ class ErrormessagesWithFeatures(unittest.TestCase):
         self.assertTrue(self.probe in cm.exception.args[0])
 
     def testCorrectErrorMessagesSnake(self):
-        from __feature__ import snake_case
+        from __feature__ import snake_case  # noqa
         with self.assertRaises(TypeError) as cm:
             QLabel().set_font(42)
         print("\n\n" + cm.exception.args[0])
         self.assertTrue(self.probe in cm.exception.args[0])
 
     def testCorrectErrorMessagesProp(self):
-        from __feature__ import true_property
+        from __feature__ import true_property  # noqa
         with self.assertRaises(TypeError) as cm:
             QLabel().font = 42
         print("\n\n" + cm.exception.args[0])
         self.assertTrue(self.probe in cm.exception.args[0])
 
     def testCorrectErrorMessagesSnakeProp(self):
-        from __feature__ import snake_case, true_property
+        from __feature__ import snake_case, true_property  # noqa
         with self.assertRaises(TypeError) as cm:
             QLabel().font = 42
         print("\n\n" + cm.exception.args[0])
         self.assertTrue(self.probe in cm.exception.args[0])
 
     def testCorrectErrorMessagesClassProp(self):
-        from __feature__ import true_property
+        from __feature__ import true_property  # noqa
         with self.assertRaises(TypeError) as cm:
             QApplication.quitOnLastWindowClosed = object
         print("\n\n" + cm.exception.args[0])
         self.assertTrue(self.probe in cm.exception.args[0])
         with self.assertRaises(TypeError) as cm:
-            qApp.quitOnLastWindowClosed = object
+            qApp.quitOnLastWindowClosed = object  # noqa
         self.assertTrue(self.probe in cm.exception.args[0])
 
     def testCorrectErrorMessagesClassSnakeProp(self):
-        from __feature__ import snake_case, true_property
+        from __feature__ import snake_case, true_property  # noqa
         with self.assertRaises(TypeError) as cm:
-            QApplication.quit_on_last_window_closed = object
+            QApplication.quit_on_last_window_closed = object  # noqa
         print("\n\n" + cm.exception.args[0])
         self.assertTrue(self.probe in cm.exception.args[0])
         with self.assertRaises(TypeError) as cm:
index c55bcaf92750591b599cb1f5a10b1c6e538e9948..c3e1aba0f58d22bfca5a07adf3bd290730f32e37 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
     QMetaObject, QObject, QPoint, QRect,
@@ -49,4 +50,3 @@ class Ui_MainWindow(object):
         MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
         self.pushButton.setText(QCoreApplication.translate("MainWindow", u"PushButton", None))
     # retranslateUi
-
index 0b85824eaa4c1ca22c540d9dba967793f296c814..54a12dc975c858a9c344e813d5bd19eaa9a99113 100644 (file)
@@ -34,7 +34,7 @@
      <x>0</x>
      <y>0</y>
      <width>263</width>
-     <height>23</height>
+     <height>26</height>
     </rect>
    </property>
   </widget>
index 2ffc9970eeeeab49bca40744e6daf6e5762eecc4..613369c38c68ddc4965b6e31f35bbdc85a96346f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
 
 """
 feature_with_uic_test.py
@@ -22,12 +23,12 @@ init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
 
-from PySide6.QtCore import QCoreApplication, QLibraryInfo, qVersion
-from PySide6.QtWidgets import QApplication, QMainWindow
+from PySide6.QtCore import QCoreApplication, qVersion
+from PySide6.QtWidgets import QMainWindow
 
 # PYSIDE-535: We cannot use __feature__ in PyPy, yet
 try:
-    from __feature__ import snake_case
+    from __feature__ import snake_case  # noqa
 
     from feature_with_uic.window import Ui_MainWindow
     have_feature = True
index aee2f516c0e44e46efd8e0bd41c17118bceb1fbc..91edc5a093aa860bb5a4a21b0b1ff0d9fe4ffe92 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 1422616d857967767f3d666418fe0e8c9a5b3e9b..9008c21618796319c4f56370f253126f98069a20 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index a808f0c3d91a75cdf6186cc63d37b569df1ff49f..2879b88740397beca6b9694487ea32018ca2c0a7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """Tests for category logging macros qCDebug, qCInfo, qCWarning, qCCritical"""
 
index 95952dfbfd3e6bd44df8f30bc8f52949ff48c514..64f3c0bb3c5cb37f020eb7bee8031ca4d5ac5cb7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 7cf6d3e3e3d72b9b22a2db5a3a391f3f60f8e7da..020a0263a32fbc77d6f1c7e2db05dd1736289648 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''(Very) Simple test case for missing names from QtCore'''
 
index c7e465b24bd5cc6743709468baa385d03eb13c00..b588148d10cf37f43bd57eb3408d20b18ae77a01 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # Test case for PySide bug 634
 # http://bugs.pyside.org/show_bug.cgi?id=634
@@ -17,7 +18,7 @@ init_test_paths(False)
 from PySide6.QtCore import QCoreApplication
 
 
-class Mock(object):
+class Mock:
     def __init__(self):
         self.called = False
         self.return_value = None
@@ -37,4 +38,3 @@ class MockClassTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 0f51ace6b690a49b61cf02328d8de84c72d150a1..1a31dda22691d4eb5f38d5806e7e76124d58db73 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import sys
index 28a271b61544ce81f427127881ee78c5e71b57e7..e023ee5b57ba1216e21a362aec0b21214711cef1 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QLineF'''
 
index 2b22b1e1705a3e0dda89ab94be83919cd8975e3b..5f08fe4bca9aecd98103a3f0bd5a4143ac50fdfb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index bb9a2222a4cba6bb9379ac2cb7adb56060f3a685..ef729650cfbbb34b67d4e2e966ffe93b6feb4699 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -19,15 +20,25 @@ class MyModel (QAbstractListModel):
     pass
 
 
+class MyMultiDataModel(QAbstractListModel):
+
+    def multiData(self, index, roleSpans):
+        if index.isValid():
+            for rd in roleSpans:
+                if rd.role() == Qt.ItemDataRole.DisplayRole:
+                    rd.setData(f"test {index.row()} {index.column()}")
+
+
 class TestQModelIndexInternalPointer(unittest.TestCase):
 
     def testInternalPointer(self):
         m = MyModel()
         foo = QObject()
         idx = m.createIndex(0, 0, foo)
-        check = m.checkIndex(idx, QAbstractItemModel.CheckIndexOption.IndexIsValid
-                                  | QAbstractItemModel.CheckIndexOption.DoNotUseParent
-                                  | QAbstractItemModel.CheckIndexOption.ParentIsInvalid)
+        flags = (QAbstractItemModel.CheckIndexOption.IndexIsValid
+                 | QAbstractItemModel.CheckIndexOption.DoNotUseParent
+                 | QAbstractItemModel.CheckIndexOption.ParentIsInvalid)
+        check = m.checkIndex(idx, flags)
         self.assertTrue(check)
 
     def testPassQPersistentModelIndexAsQModelIndex(self):
@@ -39,14 +50,18 @@ class TestQModelIndexInternalPointer(unittest.TestCase):
     def testQIdentityProxyModel(self):
         sourceModel = QStringListModel(['item1', 'item2'])
         sourceIndex = sourceModel.index(0, 0)
-        sourceData = str(sourceModel.data(sourceIndex, Qt.DisplayRole))
+        sourceData = str(sourceModel.data(sourceIndex, Qt.ItemDataRole.DisplayRole))
         proxyModel = QIdentityProxyModel()
         proxyModel.setSourceModel(sourceModel)
         proxyIndex = proxyModel.mapFromSource(sourceIndex)
-        proxyData = str(proxyModel.data(proxyIndex, Qt.DisplayRole))
+        proxyData = str(proxyModel.data(proxyIndex, Qt.ItemDataRole.DisplayRole))
         self.assertEqual(sourceData, proxyData)
 
+    def testMultiDataModel(self):
+        """Test whether QAbstractItemModel.multiData() can be implemented
+           using QModelRoleData/QModelRoleDataSpan (ATM syntax only)."""
+        model = MyMultiDataModel()  # noqa: F841
+
 
 if __name__ == '__main__':
     unittest.main()
-
index 9b9b0e62252daedfe5d1a9085f6acd629ebfcf7f..f3eec76bfe170073a144cb77527e911f85fc452a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 70452eab1fd7fb13dfa5722583ceafe4744dafd2..4e8908b4c2d7007205385f9cf6426875eb56c45c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests if QBitArray class is iterable and also '~' (__invert__) and bitwise operators'''
 
@@ -61,8 +62,8 @@ class QBitArrayIsIterableTest(unittest.TestCase):
         # Tests if QBitArray class is iterable
         qbitarray_is_iterable = True
         try:
-            bitarray = [bit for bit in self.qbitarray]
-        except:
+            bitarray = [bit for bit in self.qbitarray]  # noqa F841
+        except:  # noqa: E722
             qbitarray_is_iterable = False
         self.assertTrue(qbitarray_is_iterable)
 
@@ -80,8 +81,8 @@ class QBitArrayIsIterableTest(unittest.TestCase):
         ored_qbitarray, ored_bool_list = None, None
         try:
             ored_qbitarray = self.qbitarray | self.other_qbitarray
-            ored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]
-        except:
+            ored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]  # noqa: E501
+        except:  # noqa: E722
             has_or_bitwise_operator = False
         self.assertTrue(has_or_bitwise_operator)
         self.assertEqual(bool_list_from_qbitarray(ored_qbitarray), ored_bool_list)
@@ -92,8 +93,8 @@ class QBitArrayIsIterableTest(unittest.TestCase):
         anded_qbitarray, anded_bool_list = None, None
         try:
             anded_qbitarray = self.qbitarray | self.other_qbitarray
-            anded_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]
-        except:
+            anded_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]  # noqa: E501
+        except:  # noqa: E722
             has_and_bitwise_operator = False
         self.assertTrue(has_and_bitwise_operator)
         self.assertEqual(bool_list_from_qbitarray(anded_qbitarray), anded_bool_list)
@@ -104,8 +105,8 @@ class QBitArrayIsIterableTest(unittest.TestCase):
         xored_qbitarray, xored_bool_list = None, None
         try:
             xored_qbitarray = self.qbitarray | self.other_qbitarray
-            xored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]
-        except:
+            xored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]  # noqa: E501
+        except:  # noqa: E722
             has_xor_bitwise_operator = False
         self.assertTrue(has_xor_bitwise_operator)
         self.assertEqual(bool_list_from_qbitarray(xored_qbitarray), xored_bool_list)
@@ -131,4 +132,3 @@ class QBitArrayGetItemTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index aea9778d7bef37607e5a38f41ed8db7307f5e31e..64c2a1d69bd20b1d6283252d203eb698df198834 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QByteArray concatenation with '+' operator'''
 
@@ -28,7 +29,6 @@ class QByteArrayConcatenationOperatorTest(unittest.TestCase):
 
     def testConcatPythonStringAndQByteArray(self):
         # Test concatenation of a Python bytes with a QByteArray, in this order
-        concat_python_string_add_qbytearray_worked = True
         qba = QByteArray(bytes('foo', "UTF-8"))
         result = bytes('bar\x00', "UTF-8") + qba
         self.assertEqual(type(result), QByteArray)
@@ -37,4 +37,3 @@ class QByteArrayConcatenationOperatorTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index a2201a71635269ea26d87200a71cb1da1fd84ae9..a319d604d6387686d45b9c996b92cb365d6dfbc7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -14,7 +15,7 @@ from PySide6.QtCore import QByteArray
 from helper.docmodifier import DocModifier
 
 
-class BaseQByteArrayOperatorIAdd(object):
+class BaseQByteArrayOperatorIAdd:
     '''Base class for QByteArray += operator tests.
 
     Implementing classes should inherit from unittest.TestCase and implement
index 0064e4dd61ddf06dba7ab8906e61def5f5267ccb..c0e8b55e3b25456d4d420b486d2313f6784a59e7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QByteArray operators'''
 
index cb8f9a43102f53e61cf6241ec8467c7857e3a99d..cb0fbbb682e0838b9f5d652f270c901ada3371cb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QByteArray'''
 
@@ -107,7 +108,7 @@ class QByteArrayOnQDataStream(unittest.TestCase):
     '''
     def testIt(self):
         a = QByteArray()
-        b = QDataStream(a, QIODevice.WriteOnly)
+        b = QDataStream(a, QIODevice.OpenModeFlag.WriteOnly)
         b.writeUInt16(5000)
         # The __repr__ not suppose to crash anymore
         self.assertNotEqual(repr(b), None)
index 0137677f32409986fbaa469eaf24530e21745645..0861cd2693d9bdb5a252a05f831dffd75c691f29 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QCalendar (5.14)'''
 
index 312c9e5c7eba4204987f9e8f95bb3b59611ac273..67c23dc5d596b279a60ebcbe933b4751c6cf395a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QCbor'''
 
@@ -14,8 +15,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import (QByteArray, QCborStreamReader, QCborStreamWriter,
-                            QCborTag, QCborValue)
+from PySide6.QtCore import QByteArray, QCborStreamReader, QCborStreamWriter, QCborValue
 
 
 class TestCbor(unittest.TestCase):
@@ -32,7 +32,7 @@ class TestCbor(unittest.TestCase):
         value = reader.toInteger()
         self.assertEqual(value, 42)
 
-    def testReader(self):
+    def anotherTestReader(self):
         ba = QByteArray()
         writer = QCborStreamWriter(ba)
         writer.append("hello")
@@ -42,15 +42,10 @@ class TestCbor(unittest.TestCase):
         self.assertTrue(not ba.isEmpty())
         reader = QCborStreamReader(ba)
         self.assertTrue(reader.hasNext())
-        if (reader.isByteArray()):  # Python 2
-            value = reader.readByteArray()
-            self.assertTrue(value)
-            self.assertEqual(value.data, "hello")
-        else:
-            self.assertTrue(reader.isString())
-            value = reader.readString()
-            self.assertTrue(value)
-            self.assertEqual(value.data, "hello")
+        self.assertTrue(reader.isString())
+        value = reader.readString()
+        self.assertTrue(value)
+        self.assertEqual(value.data, "hello")
 
     def testValue(self):
         value = QCborValue('hello')
index 9d1052b249ed9aa28d088fd151b3184dd1b47f7b..ba43c904d4eda8cc0cff070e0bc715a1e127cf4f 100644 (file)
@@ -1,10 +1,10 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QCollator'''
 
-import ctypes
 import os
 import sys
 import unittest
@@ -20,23 +20,23 @@ from PySide6.QtCore import QCollator, QLocale, Qt
 class QCollatorTest(unittest.TestCase):
     def testState(self):
         c = QCollator()
-        c.setCaseSensitivity(Qt.CaseInsensitive)
-        c.setLocale(QLocale.German)
+        c.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
+        c.setLocale(QLocale.Language.German)
 
         print("compare a and b:", c.compare("a", "b"))
 
-        self.assertEqual(c.caseSensitivity(), Qt.CaseInsensitive)
-        self.assertEqual(c.locale(), QLocale(QLocale.German))
+        self.assertEqual(c.caseSensitivity(), Qt.CaseSensitivity.CaseInsensitive)
+        self.assertEqual(c.locale(), QLocale(QLocale.Language.German))
 
-        c.setLocale(QLocale.French)
+        c.setLocale(QLocale.Language.French)
         c.setNumericMode(True)
         c.setIgnorePunctuation(True)
-        c.setLocale(QLocale.NorwegianBokmal)
+        c.setLocale(QLocale.Language.NorwegianBokmal)
 
-        self.assertEqual(c.caseSensitivity(), Qt.CaseInsensitive)
+        self.assertEqual(c.caseSensitivity(), Qt.CaseSensitivity.CaseInsensitive)
         self.assertEqual(c.numericMode(), True)
         self.assertEqual(c.ignorePunctuation(), True)
-        self.assertEqual(c.locale(), QLocale(QLocale.NorwegianBokmal))
+        self.assertEqual(c.locale(), QLocale(QLocale.Language.NorwegianBokmal))
 
 
 if __name__ == '__main__':
index c562065ae0b9964b6427b44fcf8ef18ab25f8fbe..c009487f7e6063559ce7365a4991123fa017cdb9 100644 (file)
@@ -1,10 +1,10 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QCommandLineParser and QCommandLineOption'''
 
-import ctypes
 import os
 import sys
 import unittest
@@ -19,7 +19,7 @@ from PySide6.QtCore import QCoreApplication, QCommandLineOption, QCommandLinePar
 
 class QCommandLineParserTest(unittest.TestCase):
     def testParser(self):
-        app = QCoreApplication([])
+        app = QCoreApplication([])  # noqa
 
         parser1 = QCommandLineParser()
         self.assertEqual(parser1.parse(["QtCore_qcommandlineparser_test", "file.txt"]), True)
index 3c850092331bea42f2cb75f716529f32f305f45d..9871eb97fd207389fa0ef8b26dafabeefc71f19a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 968765020a2cb6a5fe9f0b89234dca206643aa27..25354f07208845b13a7ab449289edcdbbccea76d 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QCoreApplication.instance static method'''
 
@@ -28,4 +29,3 @@ class QCoreApplicationInstanceTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index edd4dc8e27eac5a43341ae8729da7307714cfadd..f8ea5a0f1bd13ba09f2af61bfb00dea1ddecd0c1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 517f466aa9eb71a4edcc2668548577a01f9f0351..564a5d059b609e663824e1b4cd2903f37dd71ce5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QDataStream'''
 
@@ -27,7 +28,7 @@ def create_bitarray(string):
 
 def serialize_bitarray(bit_array):
     buffer = QByteArray()
-    stream = QDataStream(buffer, QIODevice.WriteOnly)
+    stream = QDataStream(buffer, QIODevice.OpenModeFlag.WriteOnly)
     stream << bit_array
     return buffer
 
@@ -37,8 +38,8 @@ class QDataStreamWrite(unittest.TestCase):
 
     def setUp(self):
         self.ba = QByteArray()
-        self.read = QDataStream(self.ba, QIODevice.ReadOnly)
-        self.write = QDataStream(self.ba, QIODevice.WriteOnly)
+        self.read = QDataStream(self.ba, QIODevice.OpenModeFlag.ReadOnly)
+        self.write = QDataStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
 
     def testWriteUInt8(self):
         '''QDataStream.writeUInt8 (accepting str of size 1)'''
@@ -92,8 +93,8 @@ class QDataStreamShift(unittest.TestCase):
 
     def setUp(self):
         self.ba = QByteArray()
-        self.stream = QDataStream(self.ba, QIODevice.WriteOnly)
-        self.read_stream = QDataStream(self.ba, QIODevice.ReadOnly)
+        self.stream = QDataStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
+        self.read_stream = QDataStream(self.ba, QIODevice.OpenModeFlag.ReadOnly)
 
     def testQCharValid(self):
         '''QDataStream <<>> QChar - valid'''
@@ -255,7 +256,7 @@ class QDataStreamShiftBitArray(unittest.TestCase):
         '''Check the >> operator for the given data set'''
 
         for data, expectedStatus, expectedString in data_set:
-            stream = QDataStream(data, QIODevice.ReadOnly)
+            stream = QDataStream(data, QIODevice.OpenModeFlag.ReadOnly)
             string = QBitArray()
             stream >> string
 
@@ -273,14 +274,14 @@ class QDataStreamShiftBitArray(unittest.TestCase):
 
         data = []
         for expected in test_set:
-            data.append((serialize_bitarray(expected), QDataStream.Ok, expected))
+            data.append((serialize_bitarray(expected), QDataStream.Status.Ok, expected))
         self._check_bitarray(data)
 
     def testPastEnd(self):
         '''QDataStream >> QBitArray reading past the end of the data'''
         serialized = serialize_bitarray(create_bitarray('1001110'))
         serialized.resize(serialized.size() - 2)
-        self._check_bitarray([(serialized, QDataStream.ReadPastEnd, QBitArray())])
+        self._check_bitarray([(serialized, QDataStream.Status.ReadPastEnd, QBitArray())])
 
 
 class QDataStreamBuffer(unittest.TestCase):
@@ -289,7 +290,7 @@ class QDataStreamBuffer(unittest.TestCase):
         self.assertEqual(data.readRawData(4), None)
 
         ba = QByteArray()
-        data = QDataStream(ba, QIODevice.WriteOnly)
+        data = QDataStream(ba, QIODevice.OpenModeFlag.WriteOnly)
         data.writeRawData('AB\x00C')
         self.assertEqual(ba.data(), bytes('AB\x00C', "UTF-8"))
 
@@ -300,7 +301,7 @@ class QDataStreamBuffer(unittest.TestCase):
         test_data = b'AB\0'
         data = QDataStream()
         ba = QByteArray()
-        data = QDataStream(ba, QIODevice.WriteOnly)
+        data = QDataStream(ba, QIODevice.OpenModeFlag.WriteOnly)
         data.writeRawData(test_data)
         self.assertEqual(ba.data(), test_data)
         data = QDataStream(ba)
@@ -311,7 +312,7 @@ class QDataStreamBuffer(unittest.TestCase):
         self.assertEqual(dataOne.readBytes(4), None)
 
         ba = QByteArray()
-        data = QDataStream(ba, QIODevice.WriteOnly)
+        data = QDataStream(ba, QIODevice.OpenModeFlag.WriteOnly)
         # writeBytes() writes a quint32 containing the length of the data,
         # followed by the data.
         data.writeBytes(bytes('AB\x00C', 'UTF-8'))
index 611292227d3e08c6afeec288c7c9152f68417bd1..6979c62fb5726a290da65123cdbdb77161402860 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QDate'''
 
index 96e35311233c6412267c37c20395d24994a94dff..fa9a988be6de1d279f0221e86c4cb3520613a94f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import datetime
 import os
index 702c7f13a66df906f4e14ef3b64e26db0c7f1ea7..0cc1cfb561708a79e741f3cd6117e58d27f68977 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QDir
+from PySide6.QtCore import QDir, QDirListing
 
 
 class QDirTest(unittest.TestCase):
@@ -35,5 +36,14 @@ class QDirTest(unittest.TestCase):
         self.assertEqual(b, c)
 
 
+class QDirListingTest(unittest.TestCase):
+    '''Test case for QDirListing'''
+
+    def test(self):
+        dir_listing = QDirListing(QDir.homePath())
+        for entry in dir_listing:
+            pass
+
+
 if __name__ == '__main__':
     unittest.main()
index b202dbefb82683b6f565dae5a1b9adc3e4fb3eb0..b39563bb916f117e416b01363798c11974fdd2f2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 45a8e91246eb36703193c8cea0f9206a3e9c6a21..58115295f7d99115861ceb6d91ab74ea6058e87e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QEnum and QFlags'''
 
@@ -32,8 +33,8 @@ class TestEnum(unittest.TestCase):
     def testEnumNew_NoLeak(self):
         gc.collect()
         total = sys.gettotalrefcount()
-        for idx in range(1000):
-            ret = Qt.Key(42)
+        for _ in range(1000):
+            ret = Qt.Key(42)  # noqa: F841
 
         gc.collect()
         delta = sys.gettotalrefcount() - total
@@ -49,24 +50,24 @@ class TestEnum(unittest.TestCase):
 class TestQFlags(unittest.TestCase):
 
     def testToItn(self):
-        om = QIODevice.NotOpen
+        om = QIODevice.OpenModeFlag.NotOpen
         omcmp = om.value
 
-        self.assertEqual(om, QIODevice.NotOpen)
+        self.assertEqual(om, QIODevice.OpenModeFlag.NotOpen)
         self.assertTrue(omcmp == 0)
 
-        self.assertTrue(omcmp != QIODevice.ReadOnly)
+        self.assertTrue(omcmp != QIODevice.OpenModeFlag.ReadOnly)
         self.assertTrue(omcmp != 1)
 
     def testToIntInFunction(self):
-        om = QIODevice.WriteOnly
+        om = QIODevice.OpenModeFlag.WriteOnly
         self.assertEqual(int(om.value), 2)
 
     def testNonExtensibleEnums(self):
         try:
-            om = QIODevice.OpenMode(QIODevice.WriteOnly)
+            om = QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly)  # noqa: F841
             self.assertFail()
-        except:
+        except:  # noqa: E722
             pass
 
 
@@ -75,8 +76,8 @@ class TestEnumPickling(unittest.TestCase):
     def testPickleEnum(self):
 
         # Pickling of enums with different depth works.
-        ret = pickle.loads(pickle.dumps(QIODevice.Append))
-        self.assertEqual(ret, QIODevice.Append)
+        ret = pickle.loads(pickle.dumps(QIODevice.OpenModeFlag.Append))
+        self.assertEqual(ret, QIODevice.OpenModeFlag.Append)
 
         ret = pickle.loads(pickle.dumps(Qt.Key.Key_Asterisk))
         self.assertEqual(ret, Qt.Key.Key_Asterisk)
@@ -87,9 +88,7 @@ class TestEnumPickling(unittest.TestCase):
 
         # This works also with nested classes for Python 3, after we
         # introduced the correct __qualname__ attribute.
-
-        # Note: For Python 2, we would need quite strange patches.
-        func = lambda: pickle.loads(pickle.dumps(Qt.Key))
+        func = lambda: pickle.loads(pickle.dumps(Qt.Key))  # noqa: E731
         func()
 
 # PYSIDE-957: The QEnum macro
@@ -100,7 +99,7 @@ try:
     HAVE_ENUM = True
 except ImportError:
     HAVE_ENUM = False
-    QEnum = QFlag = lambda x: x
+    QEnum = QFlag = lambda x: x  # noqa: F811
     import types
 
     class Enum:
@@ -143,7 +142,7 @@ class SomeClass(QObject):
         class InnerEnum(enum.Enum):
             X = 42
 
-    class SomeEnum(enum.Enum):
+    class SomeEnum(enum.Enum):  # noqa: F811
         A = 4
         B = 5
         C = 6
@@ -165,10 +164,9 @@ class TestQEnumMacro(unittest.TestCase):
             int(SomeClass.SomeEnum.C) == 6
         self.assertEqual(SomeClass.OtherEnum.C, 3)
 
-    @unittest.skipIf(sys.version_info[0] < 3, "we cannot support nested classes in Python 2")
     def testInnerClass(self):
         self.assertEqual(SomeClass.InnerClass.InnerEnum.__qualname__,
-            "SomeClass.InnerClass.InnerEnum")
+                         "SomeClass.InnerClass.InnerEnum")
         with self.assertRaises(TypeError):
             int(SomeClass.InnerClass.InnerEnum.X) == 42
 
@@ -193,8 +191,8 @@ class TestQEnumMacro(unittest.TestCase):
         self.assertEqual(mo.enumerator(1).name(), "SomeEnum")
         moi = SomeClass.InnerClass.staticMetaObject
         self.assertEqual(moi.enumerator(0).name(), "InnerEnum")
-        ## Question: Should that scope not better be  "SomeClass.InnerClass"?
-        ## But we have __qualname__ already:
+        # Question: Should that scope not better be  "SomeClass.InnerClass"?
+        # But we have __qualname__ already:
         self.assertEqual(moi.enumerator(0).scope(), "InnerClass")
 
 
index 244a14f1300ab13dc64e62e93934329c238b761d..5527e4c6b5303c531ba830e4b2bd508b303300fe 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtCore.QEvent'''
 
@@ -22,14 +23,14 @@ class QEventTypeFlag(unittest.TestCase):
     def testFlagAccess(self):
         # QEvent.Type flags usage
 
-        event = QEvent(QEvent.Timer)
-        self.assertEqual(event.type(), QEvent.Timer)
+        event = QEvent(QEvent.Type.Timer)
+        self.assertEqual(event.type(), QEvent.Type.Timer)
 
-        event = QEvent(QEvent.Close)
-        self.assertEqual(event.type(), QEvent.Close)
+        event = QEvent(QEvent.Type.Close)
+        self.assertEqual(event.type(), QEvent.Type.Close)
 
-        event = QEvent(QEvent.IconTextChange)
-        self.assertEqual(event.type(), QEvent.IconTextChange)
+        event = QEvent(QEvent.Type.IconTextChange)
+        self.assertEqual(event.type(), QEvent.Type.IconTextChange)
 
 
 if __name__ == '__main__':
index 0af5c265047bfe804096d5688f45753cfedc8563..ffac439752c48dd7a30e295eb97b303e12655ccb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -30,7 +31,7 @@ class GetCharTest(unittest.TestCase):
     def testBasic(self):
         '''QFile.getChar'''
         obj = QFile(self.filename)
-        obj.open(QIODevice.ReadOnly)
+        obj.open(QIODevice.OpenModeFlag.ReadOnly)
         try:
             self.assertEqual(obj.getChar(), (True, 'a'))
             self.assertFalse(obj.getChar()[0])
@@ -39,7 +40,7 @@ class GetCharTest(unittest.TestCase):
 
     def testBug721(self):
         obj = QFile(self.filename)
-        obj.open(QIODevice.ReadOnly)
+        obj.open(QIODevice.OpenModeFlag.ReadOnly)
         try:
             memory = obj.map(0, 1)
             self.assertEqual(len(memory), 1)
@@ -55,7 +56,7 @@ class GetCharTest(unittest.TestCase):
         dir = QTemporaryDir(QDir.tempPath() + "/XXXXXX.dir")
         self.assertTrue(dir.isValid())
         saveFile = QSaveFile(dir.path() + "/test.dat")
-        self.assertTrue(saveFile.open(QIODevice.WriteOnly))
+        self.assertTrue(saveFile.open(QIODevice.OpenModeFlag.WriteOnly))
         saveFile.write(bytes("Test", "UTF-8"))
         self.assertTrue(saveFile.commit())
         self.assertTrue(os.path.exists(QDir.toNativeSeparators(saveFile.fileName())))
index dfdfd7fa2681f31d1edbaae705839cdf467ea5ee..3f91b0596a667208acdb609bbf621cff7c95acbb 100644 (file)
@@ -1,9 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
-import tempfile
 import unittest
 
 from pathlib import Path
@@ -19,7 +19,7 @@ class QFileConstructor(unittest.TestCase):
 
     def testBasic(self):
         '''QFileInfo(QFile)'''
-        obj = QFileInfo(QFile())
+        obj = QFileInfo(QFile())  # noqa
 
     def testQFileInfoPath(self):
         # PYSIDE-1499: Test QFileInfo with Path objects
index c66a27f8a3319970e00b55620e2ee4e1b6ac7882..5caab503509db06682a09c98f3af57cd29ac771f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QIODevice, QTemporaryFile
+from PySide6.QtCore import QTemporaryFile
 
 
 class FileChild1(QTemporaryFile):
index 2a53066853654d6e018e12811c59abce241429fb..f5722fd0dcf40553ca897d1ae5cf9098fe81c263 100644 (file)
@@ -1,10 +1,10 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QFlags'''
 
-import operator
 import os
 import sys
 import unittest
@@ -27,12 +27,16 @@ class QFlagTest(unittest.TestCase):
         f.close()
 
         f = QFile(fileName)
-        self.assertEqual(f.open(QIODevice.Truncate | QIODevice.Text | QIODevice.ReadWrite), True)
+        of = (QIODevice.OpenModeFlag.Truncate | QIODevice.OpenModeFlag.Text
+              | QIODevice.OpenModeFlag.ReadWrite)
+        self.assertEqual(f.open(of), True)
         om = f.openMode()
-        self.assertEqual(om & QIODevice.Truncate, QIODevice.Truncate)
-        self.assertEqual(om & QIODevice.Text, QIODevice.Text)
-        self.assertEqual(om & QIODevice.ReadWrite, QIODevice.ReadWrite)
-        self.assertTrue(om == QIODevice.Truncate | QIODevice.Text | QIODevice.ReadWrite)
+        self.assertEqual(om & QIODevice.OpenModeFlag.Truncate, QIODevice.OpenModeFlag.Truncate)
+        self.assertEqual(om & QIODevice.OpenModeFlag.Text, QIODevice.OpenModeFlag.Text)
+        self.assertEqual(om & QIODevice.OpenModeFlag.ReadWrite, QIODevice.OpenModeFlag.ReadWrite)
+        expected = (QIODevice.OpenModeFlag.Truncate | QIODevice.OpenModeFlag.Text
+                    | QIODevice.OpenModeFlag.ReadWrite)
+        self.assertTrue(om == expected)
         f.close()
 
 
@@ -41,53 +45,57 @@ class QFlagOperatorTest(unittest.TestCase):
 
     def testInvert(self):
         '''QFlags ~ (invert) operator'''
-        self.assertEqual(type(~QIODevice.ReadOnly), QIODevice.OpenMode)
+        self.assertEqual(type(~QIODevice.OpenModeFlag.ReadOnly), QIODevice.OpenMode)
 
     def testOr(self):
         '''QFlags | (or) operator'''
-        self.assertEqual(type(QIODevice.ReadOnly | QIODevice.WriteOnly), QIODevice.OpenMode)
+        self.assertEqual(type(QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.WriteOnly),
+                         QIODevice.OpenMode)
 
     def testAnd(self):
         '''QFlags & (and) operator'''
-        self.assertEqual(type(QIODevice.ReadOnly & QIODevice.WriteOnly), QIODevice.OpenMode)
+        self.assertEqual(type(QIODevice.OpenModeFlag.ReadOnly & QIODevice.OpenModeFlag.WriteOnly),
+                         QIODevice.OpenMode)
 
     def testIOr(self):
         '''QFlags |= (ior) operator'''
         flag = Qt.WindowFlags()
-        self.assertTrue(Qt.Widget == 0)
-        self.assertFalse(flag & Qt.Widget)
-        result = flag & Qt.Widget
+        self.assertTrue(Qt.WindowType.Widget == 0)
+        self.assertFalse(flag & Qt.WindowType.Widget)
+        result = flag & Qt.WindowType.Widget
         self.assertTrue(result == 0)
-        flag |= Qt.WindowMinimizeButtonHint
-        self.assertTrue(flag & Qt.WindowMinimizeButtonHint)
+        flag |= Qt.WindowType.WindowMinimizeButtonHint
+        self.assertTrue(flag & Qt.WindowType.WindowMinimizeButtonHint)
 
     def testInvertOr(self):
         '''QFlags ~ (invert) operator over the result of an | (or) operator'''
-        self.assertEqual(type(~(Qt.ItemIsSelectable | Qt.ItemIsEditable)), Qt.ItemFlags)
+        self.assertEqual(type(~(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEditable)),
+                         Qt.ItemFlags)
 
     def testEqual(self):
         '''QFlags == operator'''
-        flags = Qt.Window
-        flags |= Qt.WindowMinimizeButtonHint
-        flag_type = (flags & Qt.WindowType_Mask)
-        self.assertEqual(flag_type, Qt.Window)
+        flags = Qt.WindowType.Window
+        flags |= Qt.WindowType.WindowMinimizeButtonHint
+        flag_type = (flags & Qt.WindowType.WindowType_Mask)
+        self.assertEqual(flag_type, Qt.WindowType.Window)
 
-        self.assertEqual(Qt.KeyboardModifiers(Qt.ControlModifier), Qt.ControlModifier)
+        self.assertEqual(Qt.KeyboardModifiers(Qt.KeyboardModifier.ControlModifier),
+                         Qt.KeyboardModifier.ControlModifier)
 
     def testOperatorBetweenFlags(self):
         '''QFlags & QFlags'''
-        flags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
-        newflags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
+        flags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
+        newflags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
         self.assertTrue(flags & newflags)
 
     def testOperatorDifferentOrder(self):
         '''Different ordering of arguments'''
-        flags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
-        self.assertEqual(flags | Qt.ItemIsEnabled, Qt.ItemIsEnabled | flags)
+        flags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
+        self.assertEqual(flags | Qt.ItemFlag.ItemIsEnabled, Qt.ItemFlag.ItemIsEnabled | flags)
 
     def testEqualNonNumericalObject(self):
         '''QFlags ==,!= non-numerical object '''
-        flags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
+        flags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
 
         self.assertTrue(flags != None)  # noqa: E711
         self.assertFalse(flags == None)  # noqa: E711
@@ -108,7 +116,7 @@ class QFlagOperatorTest(unittest.TestCase):
 class QFlagsOnQVariant(unittest.TestCase):
     def testQFlagsOnQVariant(self):
         o = QObject()
-        o.setProperty("foo", QIODevice.ReadOnly | QIODevice.WriteOnly)
+        o.setProperty("foo", QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.WriteOnly)
         self.assertEqual(type(o.property("foo")), QIODevice.OpenMode)
 
 
index e0f4f3f7acb01b96dbadda95c3b2af2340e110b9..71a8314b27d8ce1033a24e1fa9e693e1764fc24c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QEnum and QFlags'''
 
@@ -13,7 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QThread, Qt
+from PySide6.QtCore import QThread
 
 
 class TestHANDLE(unittest.TestCase):
index 9c5ae43703331f48afae54f6a43ddc02432f26c0..873512796a41eed1e09c70e6f546a0c2782e9f21 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for qInstallMsgHandler'''
 
@@ -45,7 +46,7 @@ class QInstallMsgHandlerTest(unittest.TestCase):
         ret = qInstallMessageHandler(None)
         self.assertEqual(ret, None)
         refcount = sys.getrefcount(handleruseless)
-        retNone = qInstallMessageHandler(handleruseless)
+        retNone = qInstallMessageHandler(handleruseless)  # noqa: F841
         self.assertEqual(sys.getrefcount(handleruseless), refcount + 1)
         rethandler = qInstallMessageHandler(None)
         self.assertEqual(rethandler, handleruseless)
@@ -53,7 +54,7 @@ class QInstallMsgHandlerTest(unittest.TestCase):
         self.assertEqual(sys.getrefcount(handleruseless), refcount)
 
     def testHandler(self):
-        rethandler = qInstallMessageHandler(handler)
+        rethandler = qInstallMessageHandler(handler)  # noqa: F841
         if QLibraryInfo.isDebugBuild():
             qDebug("Test Debug")
             self.assertEqual(param[0], QtMsgType.QtDebugMsg)
@@ -73,4 +74,3 @@ class QInstallMsgHandlerTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index ea735112a6ba9f79792a31db32db1ef7ab8a7f77..a20e41be853c7b954061c6f6dbb524036d416fce 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for buffered read methods of QIODevice'''
 
index 53a6ba70af627cde4f24c0939fb3ee0be02d749d..908a3a892c8d72f3f6910b490631823599d3bab4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for the QIOPipe class'''
 
index a50f185ff9aaa8a19d45b9c7c01c1dcbf2a0a326..63683fd3f7f3b0aacc086669d9921c0a04bf1ebd 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QJsonDocument/nullptr_t'''
 
index 90ae50159a6f8a7f863fdfcfe538518338f12b99..40df326bd60f75f0f71b50793a11e207e8c9faf5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QLineF'''
 
@@ -23,7 +24,7 @@ class TestQLineF (unittest.TestCase):
         tuple_ = l1.intersects(l2)
         self.assertEqual(tuple, tuple_.__class__)
         (value, p) = tuple_
-        self.assertEqual(QLineF.BoundedIntersection, value)
+        self.assertEqual(QLineF.IntersectionType.BoundedIntersection, value)
         self.assertEqual(QPointF(1, 0), p)
 
 
index 05b0d3219b12b70cc42926e5dcd94265f5db94dc..8723cf000a6535cc215946c3beec4d5100ae823e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QLocale'''
 
@@ -19,36 +20,36 @@ from PySide6.QtCore import QLocale
 
 class QLocaleTestToNumber(unittest.TestCase):
     def testToNumberInt(self):
-        obj = QLocale(QLocale.C)
+        obj = QLocale(QLocale.Language.C)
         self.assertEqual((37, True), obj.toInt('37'))
 
     def testToNumberFloat(self):
-        obj = QLocale(QLocale.C)
+        obj = QLocale(QLocale.Language.C)
         self.assertEqual((ctypes.c_float(37.109).value, True),
                          obj.toFloat('37.109'))
 
     def testToNumberDouble(self):
-        obj = QLocale(QLocale.C)
+        obj = QLocale(QLocale.Language.C)
         self.assertEqual((ctypes.c_double(37.109).value, True),
                          obj.toDouble('37.109'))
 
     def testToNumberShort(self):
-        obj = QLocale(QLocale.C)
+        obj = QLocale(QLocale.Language.C)
         self.assertEqual((ctypes.c_short(37).value, True),
                          obj.toShort('37'))
 
     def testToNumberLong(self):
-        obj = QLocale(QLocale.C)
+        obj = QLocale(QLocale.Language.C)
         self.assertEqual((ctypes.c_long(37).value, True),
                          obj.toLong('37'))
 
     def testToNumberULongLong(self):
-        obj = QLocale(QLocale.C)
+        obj = QLocale(QLocale.Language.C)
         self.assertEqual((ctypes.c_ulonglong(37).value, True),
                          obj.toULongLong('37'))
 
     def testToNumberULongLongNegative(self):
-        obj = QLocale(QLocale.C)
+        obj = QLocale(QLocale.Language.C)
         self.assertTrue(not obj.toULongLong('-37')[1])
 
     def testToCurrencyString(self):
index 6dca9235cc1b6963ec4980375b9034fcb361c03c..e6e614b2649074d258755431a577b8c35e014d99 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QLockFile'''
 
index 74e6bceb9f3070a65c096e158bb8589ab1d37c0d..685838e46046265899ca81c7dd0bba0817ebb6be 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QMessageAuthenticationCode'''
 
@@ -18,7 +19,8 @@ from PySide6.QtCore import QCryptographicHash, QMessageAuthenticationCode
 
 class TestQMessageAuthenticationCode (unittest.TestCase):
     def test(self):
-        code = QMessageAuthenticationCode(QCryptographicHash.Sha1, bytes('bla', "UTF-8"))
+        code = QMessageAuthenticationCode(QCryptographicHash.Algorithm.Sha1,
+                                          bytes('bla', "UTF-8"))
         result = code.result()
         self.assertTrue(result.size() > 0)
         print(result.toHex())
index 4b3051711a4974a8e5d9df1b0bb7e634cb0cb3bd..39b4f36a00f0b660a287a95acbf9f11135ccb614 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for static methos conflicts with class methods'''
 
@@ -119,7 +120,7 @@ class qmetaobject_test(unittest.TestCase):
         app = QCoreApplication()  # noqa: F841
         sender = SemaphoreSender()
         receiver = SemaphoreReceiver()
-        sender.signal.connect(receiver.receiverSlot, Qt.QueuedConnection)
+        sender.signal.connect(receiver.receiverSlot, Qt.ConnectionType.QueuedConnection)
         sender.emitSignal()
         while not receiver.semaphore:
             QCoreApplication.processEvents()
index f8e0ba1c0a30e8ae950c6823b64d6ba96e90c260..cdd02e5ba688b839e3fc16b46d82e950832924e2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for QMetaType'''
 
index 6a63edb859b88b151c2b88f4a864b18a289b2953..344c71da79694d670279fe9b5c7467931658ffb2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QMimeDatabase'''
 
@@ -32,15 +33,16 @@ class QMimeDatabaseTest(unittest.TestCase):
         self.assertTrue(s1.isValid())
         self.assertEqual(s1.name(), "text/plain")
 
-        krita = db.mimeTypeForName("application/x-krita")
-        self.assertTrue(krita.isValid())
+        # Removed because of the move of to the Tika mimetypes.
+        # krita = db.mimeTypeForName("application/x-krita")
+        # self.assertTrue(krita.isValid())
 
         rdf = db.mimeTypeForName("application/rdf+xml")
         self.assertTrue(rdf.isValid())
         self.assertEqual(rdf.name(), "application/rdf+xml")
         self.assertTrue(rdf.comment())
         if "en" in QLocale().name():
-            self.assertEqual(rdf.comment(), "RDF file")
+            self.assertTrue(rdf.comment() in ("RDF file", "XML syntax for RDF graphs"))
 
         bzip2 = db.mimeTypeForName("application/x-bzip2")
         self.assertTrue(bzip2.isValid())
index ec37ca6a0534de8fd6e6f3582c639a0d5fb87af4..bff586cc27669f0f7e64867ae23648efde7cee1a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test case for QAbstractListModel.createIndex and QModelIndex.internalPointer'''
 
@@ -36,7 +37,7 @@ class TestQModelIndexInternalPointer(unittest.TestCase):
     def testInternalPointer(self):
         # Test QAbstractListModel.createIndex and
         # QModelIndex.internalPointer with regular Python objects
-        obj = QObject()
+        obj = QObject()  # noqa: F841
         idx = self.model.createIndex(0, 0, "Hello")
         i = idx.internalPointer()
         self.assertEqual(i, "Hello")
@@ -48,7 +49,7 @@ class TestQModelIndexInternalPointer(unittest.TestCase):
         o = [1, 2, 3]
         o_refcnt = sys.getrefcount(o)
         idx = self.model.createIndex(0, 0, o)
-        ptr = idx.internalPointer()
+        ptr = idx.internalPointer()  # noqa: F841
         self.assertEqual(sys.getrefcount(o), o_refcnt + 1)
 
     def testIndexForDefaultDataArg(self):
@@ -60,4 +61,3 @@ class TestQModelIndexInternalPointer(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 508526ccade16e62123ccf660efc3fbbd7f3484d..4bdd3922bd2f3c2d3075c09bbde898dcdfddf7d4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for parent-child relationship'''
 
index b3bfaf007fedbbb2cceb9959bd19c756755d26d6..b719d4a7e52f29851531a25a00c1416a4af432d0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test case for QObject::connectNotify()'''
 
@@ -65,9 +66,6 @@ class TestQObjectConnectNotify(UsesQApplication):
         receiver = QObject()
         sender.connect(SIGNAL("destroyed()"), receiver, SLOT("deleteLater()"))
         self.assertTrue(sender.con_notified)
-        # When connecting to a regular slot, and not a python callback function, QObject::connect
-        # will use the non-cloned method signature, so connecting to destroyed() will actually
-        # connect to destroyed(QObject*).
         self.assertEqual(sender.signal.methodSignature(), "destroyed(QObject*)")
         self.assertTrue(sender.disconnect(SIGNAL("destroyed()"), receiver, SLOT("deleteLater()")))
         self.assertTrue(sender.dis_notified)
index 2b7b40f25856a9facf907ea6b3a856175ae6d247..9bb80459aeda14e73eb9f758e8d20e77be30dfd1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index ab7a1b6ad20698d62dbd3cedd59f0a4b66d9f07e..a011f7240a9936541d63b5a8002c415eb9141a91 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QObject.eventFilter'''
 
index 0bd4fba75c8a56b7979399e565c02438874177f4..e1121abd7ef9ae7cee0156789f1f6b2effc912ec 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QObject methods'''
 
@@ -43,7 +44,7 @@ class InheritsCase(unittest.TestCase):
         # QObject.inherits(classname) should fail if classname isn't a
         # QObject subclass
 
-        class Parent(object):
+        class Parent:
             # Dummy parent
             pass
 
@@ -70,6 +71,8 @@ class InheritsCase(unittest.TestCase):
 
     def testMultipleInheritance(self):
         def declareClass():
+            # Note: 'object' cannot removed from this class declaration
+            # in order to make it work with QObject.
             class Foo(object, QObject):
                 pass
 
index f1a4cb0b59c824397f8ef963fbffa0a52b3d2317..980c766b8bf4e085d865c2ad50fcacc58abe987e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for the bug #378
 http://bugs.openbossa.org/show_bug.cgi?id=378
@@ -34,9 +35,8 @@ class TestBug378(unittest.TestCase):
     '''Test case for the bug #378'''
 
     def testBug378(self):
-        obj = ExtQObject()
+        obj = ExtQObject()  # noqa: F841
 
 
 if __name__ == '__main__':
     unittest.main()
-
index 6265f77b10bf45b9f47f0fcc2af0c11d7e683075..1abc2318144a1855171e0cd6a67eec5f239e23bc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for parent-child relationship'''
 
@@ -115,19 +116,19 @@ class ParentCase(unittest.TestCase):
         search_result = parent.findChild(QObject, nested_child_name)
         self.assertTrue(search_result)
         search_result = parent.findChild(QObject, nested_child_name,
-                                         Qt.FindChildrenRecursively)
+                                         Qt.FindChildOption.FindChildrenRecursively)
         self.assertTrue(search_result)
         search_result = parent.findChild(QObject, nested_child_name,
-                                         Qt.FindDirectChildrenOnly)
+                                         Qt.FindChildOption.FindDirectChildrenOnly)
         self.assertFalse(search_result)
 
         search_results = parent.findChildren(QObject, nested_child_name)
         self.assertEqual(len(search_results), 1)
         search_result = parent.findChildren(QObject, nested_child_name,
-                                            Qt.FindChildrenRecursively)
+                                            Qt.FindChildOption.FindChildrenRecursively)
         self.assertEqual(len(search_results), 1)
         search_results = parent.findChildren(QObject, nested_child_name,
-                                             Qt.FindDirectChildrenOnly)
+                                             Qt.FindChildOption.FindDirectChildrenOnly)
         self.assertEqual(len(search_results), 0)
 
     def testFindChildWithoutName(self):
index 4efb25cb4e6ae39b3fd4673e79bde84cd220ad8a..37936205e07eccd7d728fc4a4854cbbbcbd233b7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QObject property and setProperty'''
 
index 926698b4b694342ba0d1aa2a6320a8c27d927a9b..b568ed1fa751ab68756bf0e1e2c49565979e6ebe 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QObject protected methods'''
 
@@ -35,15 +36,15 @@ class QObjectReceivers(unittest.TestCase):
         self.assertEqual(obj.receivers(SIGNAL("destroyed()")), 0)
 
         obj.destroyed.connect(self.cb)
-        self.assertEqual(obj.receivers(SIGNAL("destroyed()")), 1)
+        self.assertTrue(obj.receivers(SIGNAL("destroyed()")) > 0)
 
     def testQThreadReceiversExtern(self):
         # QThread.receivers() - Inherited protected method
 
         obj = QThread()
-        self.assertEqual(obj.receivers(SIGNAL('destroyed()')), 0)
+        old_count = obj.receivers(SIGNAL('destroyed()'))
         obj.destroyed.connect(self.cb)
-        self.assertEqual(obj.receivers(SIGNAL("destroyed()")), 1)
+        self.assertTrue(obj.receivers(SIGNAL("destroyed()")) > old_count)
 
 
 if __name__ == '__main__':
index ecfc3dff0e74589010c9385458167371763c7c89..8a20f0500f834f6c8ca385d63cbbd6bdc37703c0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QObject methods'''
 
@@ -75,8 +76,8 @@ class ObjectNameCase(unittest.TestCase):
         obj = Obj()
         # On first connect, UniqueConnection returns True, and on the second
         # it must return False, and not a RuntimeError (PYSIDE-34)
-        self.assertTrue(obj.signal.connect(obj.empty, Qt.UniqueConnection))
-        self.assertFalse(obj.signal.connect(obj.empty, Qt.UniqueConnection))
+        self.assertTrue(obj.signal.connect(obj.empty, Qt.ConnectionType.UniqueConnection))
+        self.assertFalse(obj.signal.connect(obj.empty, Qt.ConnectionType.UniqueConnection))
 
     def testDisconnect(self):
         obj = Obj()
index 9e657c27feb2c7f730cc28a2f671ea3e8129254b..58370394de46eae58746d90e6b4a2ec10d45aefe 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for QObject.timerEvent overloading'''
 
@@ -13,7 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject, QCoreApplication
+from PySide6.QtCore import QObject
 
 from helper.usesqapplication import UsesQApplication
 
index 9123c51558dd70847df0d5ad7e3c442efc2f468f..15d8db19da759aec1f6b35377b457eee48159ec7 100644 (file)
@@ -1,12 +1,12 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QObject's tr static methods.'''
 
 import gc
 import os
-import os
 import sys
 import unittest
 
@@ -51,4 +51,3 @@ class QObjectTrTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index d887029ac0136562bf9ab44e67ddbc261438712f..7365e72120ca21c65a38d1ca7a59785c2e012480 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b9d5afa37b49449bf88ed5595e1e1031ed3caf98..6af5fc084bd7844086ba01f65e448726bb7cd26f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QPoint and QPointF'''
 
@@ -18,15 +19,14 @@ from PySide6.QtCore import QPoint, QPointF
 class QPointTest(unittest.TestCase):
 
     def testQPointCtor(self):
-        point = QPoint(QPoint(10, 20))
+        point = QPoint(QPoint(10, 20))  # noqa: F841
 
 
 class QPointFTest(unittest.TestCase):
 
     def testQPointFCtor(self):
-        pointf = QPointF(QPoint(10, 20))
+        pointf = QPointF(QPoint(10, 20))  # noqa: F841
 
 
 if __name__ == '__main__':
     unittest.main()
-
index ba4d29102b385890d6a7069ca8e4b58237fb5772..1e69a15cbf1426d6a090c83755b97acf3e3ba712 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QProcess'''
 
@@ -29,7 +30,7 @@ class TestQProcess (unittest.TestCase):
         pid = p.processId()
         # We can't test the pid method result because it returns 0 when the
         # process isn't running
-        if p.state() == QProcess.Running:
+        if p.state() == QProcess.ProcessState.Running:
             self.assertNotEqual(pid, 0)
             p.waitForFinished()
         else:
index 19f2bd25127e805c6560a62215e30c3aeb5bb0c3..7d6fbc91db9e0063802363211b9650febdbd441e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index c13a8b24ad4d750e4ad410be8e1540e9a2b038b1..6ae69b6b3b4f05c0ff2ea1a70459b63d73a69674 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 8a14abc5aadd68e2ed7946179df2927ce9ec0b88..fb91654f239556705b77635ba207914db3fb60f4 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QRect'''
 
index a88c2dc2413590ea976a882a5d7340b769e0ae78..80f4c74ba281eb4e204468674aae22017db35557 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QRegularExpression'''
 
@@ -13,7 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QRegularExpression, QRegularExpressionMatch, QRegularExpressionMatchIterator
+from PySide6.QtCore import QRegularExpression
 
 
 class QRegularExpressionTest(unittest.TestCase):
@@ -27,7 +28,7 @@ class QRegularExpressionTest(unittest.TestCase):
         self.assertEqual(match.capturedView(1), 'word2')
 
     def testMatchIterator(self):
-        re = QRegularExpression('(\w+)')
+        re = QRegularExpression(r'(\w+)')
         self.assertTrue(re.isValid())
         count = 0
         it = re.globalMatch('word1 word2 word3')
index 1cdd2c785f0ccf5b2c225b3b8ddd4c3ba6759007..136d58025561d2cacbe77380f517803eb589f012 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QResource usage'''
 
@@ -13,7 +14,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QByteArray, QFile, QIODevice
-import resources_mc
+import resources_mc  # noqa: F401
 
 
 class ResourcesUsage(unittest.TestCase):
@@ -32,7 +33,7 @@ class ResourcesUsage(unittest.TestCase):
                 orig.remove(carriage_return, 1)
 
         f = QFile(':/quote.txt')  # |QIODevice.Text
-        self.assertTrue(f.open(QIODevice.ReadOnly), f.errorString())
+        self.assertTrue(f.open(QIODevice.OpenModeFlag.ReadOnly), f.errorString())
         copy = f.readAll()
         f.close()
         self.assertEqual(orig, copy)
@@ -44,7 +45,7 @@ class ResourcesUsage(unittest.TestCase):
         orig = file.read_bytes()
 
         f = QFile(':/sample.png')
-        self.assertTrue(f.open(QIODevice.ReadOnly), f.errorString())
+        self.assertTrue(f.open(QIODevice.OpenModeFlag.ReadOnly), f.errorString())
         copy = f.readAll()
         f.close()
         self.assertEqual(len(orig), len(copy))
@@ -53,4 +54,3 @@ class ResourcesUsage(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index d52f50ce1d37962e538ef4043323a292ab79265b..3dc32c0bd41f5b0ad74babeea96eb70eeeccedce 100644 (file)
@@ -1,19 +1,19 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QRunnable'''
 
 import os
 import sys
 import unittest
-from io import StringIO
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QCoreApplication, QRunnable, QThreadPool, QThread, qDebug
+from PySide6.QtCore import QRunnable, QThreadPool
 from helper.usesqapplication import UsesQApplication
 test_result = ""
 
index f54ae002938bbb21c505d3ce5902984d9aa6b207..80b99d8ab5024743aa73f48ba382ad6872fe49bd 100644 (file)
@@ -1,4 +1,3 @@
 [General]
 var1=a, b, c
 var2=a
-
index 64ceffd70cea36554a5c8dfb42242fec2c674164..e40830d4c42062d561949f8b06e8ebe408a4d044 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QDate'''
 
@@ -21,7 +22,7 @@ class TestQSettings(unittest.TestCase):
         file = Path(__file__).resolve().parent / 'qsettings_test.ini'
         self.assertTrue(file.is_file())
         file_path = QDir.fromNativeSeparators(os.fspath(file))
-        settings = QSettings(file_path, QSettings.IniFormat)
+        settings = QSettings(file_path, QSettings.Format.IniFormat)
 
         r = settings.value('var1')
         self.assertEqual(type(r), list)
@@ -50,7 +51,7 @@ class TestQSettings(unittest.TestCase):
         dir = QTemporaryDir(f'{temp_dir}/qsettings_XXXXXX')
         self.assertTrue(dir.isValid())
         file_name = dir.filePath('foo.ini')
-        settings = QSettings(file_name, QSettings.IniFormat)
+        settings = QSettings(file_name, QSettings.Format.IniFormat)
         sample_list = ["a", "b"]
         string_list_of_empty = [""]
         settings.setValue('zero_value', 0)
@@ -64,7 +65,7 @@ class TestQSettings(unittest.TestCase):
         gc.collect()
 
         # Loading values already set
-        settings = QSettings(file_name, QSettings.IniFormat)
+        settings = QSettings(file_name, QSettings.Format.IniFormat)
 
         # Getting value that doesn't exist
         r = settings.value("variable")
index d16c36da9952cd89f96fc3763fd76b5a0cf5faa7..04d1a6fd57def50978c5e76b01ca78706dfaa8a0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Client for the unit test of QSharedMemory'''
 
@@ -25,7 +26,8 @@ if __name__ == '__main__':
         print('Pass segment name', file=sys.stderr)
         sys.exit(-1)
     shared_memory = QSharedMemory(sys.argv[1])
-    if not shared_memory.attach(QSharedMemory.ReadOnly):
+    name = "I have no name"     # Fixme: What should be there?
+    if not shared_memory.attach(QSharedMemory.AccessMode.ReadOnly):
         raise SystemError(f'attach to "{name}" failed')
     if not shared_memory.lock():
         raise SystemError(f'lock of "{name}" failed')
index ed8a479390c6177d93e76805d5fb8c85be500144..5e44c0729eed376320ff889b5350fe7539132d6b 100644 (file)
@@ -1,9 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QSharedMemory'''
 
-import ctypes
 import os
 import subprocess
 import sys
index bc324ba73c4660a9a346d7ffdf56066eb5446c04..a2f71694dd939658671d4f0bbc08d33a925026d7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QSize'''
 
@@ -34,4 +35,3 @@ class QSizeOperator(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index a95afb090f31bdf8e3294dc884ae93a58c691af2..5412680df3a91c45847b27982fa14dde1353473e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 2e812947c38c75ce8ee5fefed86b0862c9c17e1a..33e58541f83552bd3e4f85d8d5642070b598682a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QUuid'''
 
@@ -19,14 +20,14 @@ from PySide6.QtCore import QCoreApplication, QSocketNotifier
 
 class QSocketNotifierTest(unittest.TestCase):
     def testClass(self):
-        app = QCoreApplication([])
+        app = QCoreApplication([])  # noqa: F841
         # socketpair is not available on Windows
         if os.name != "nt":
             w_sock, r_sock = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
 
             self.assertIsInstance(r_sock.fileno(), int)
 
-            notifier = QSocketNotifier(r_sock.fileno(), QSocketNotifier.Read)
+            notifier = QSocketNotifier(r_sock.fileno(), QSocketNotifier.Type.Read)
 
             self.assertIsNotNone(notifier)
 
index fef44261b9ff5e617326f9a3f7d164ebd3a76e66..d0e071dcfef9c54748d569907a9807dbadbf2aa5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index b8274769ba61d0ba70017745e379aab057dc2403..95f59162db64499747b8dd7511d0d252bb32453e 100644 (file)
@@ -1,10 +1,10 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QStandardPaths'''
 
-import ctypes
 import os
 import sys
 import unittest
index 5869d9f002df91dfd25e186ba07e65a76ea21175..44b3ce303e8eaa61ace773b217dc7f76a007ab76 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QStorageInfo'''
 
index d740eeeef37c9a493e6e06ad64084a256d82ea45..397dceb1eb5346e8790e0f8e3862f1ddc05efbe7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QString'''
 
index 602852fabd1f42b1a9792c6bf0e12410c4f0db94..88279428d53d578969f1bcacd8ea5c9f05560a3e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -16,12 +17,12 @@ from PySide6.QtCore import QSysInfo
 class TestQSysInfo(unittest.TestCase):
 
     def testEnumEndian(self):
-        self.assertEqual(QSysInfo.BigEndian.value, 0)
-        self.assertEqual(QSysInfo.LittleEndian.value, 1)
-        self.assertTrue(QSysInfo.ByteOrder.value > -1)
+        self.assertEqual(QSysInfo.Endian.BigEndian.value, 0)
+        self.assertEqual(QSysInfo.Endian.LittleEndian.value, 1)
+        self.assertTrue(QSysInfo.Endian.ByteOrder.value > -1)
 
     def testEnumSizes(self):
-        self.assertTrue(QSysInfo.WordSize.value > 0)
+        self.assertTrue(QSysInfo.Sizes.WordSize.value > 0)
 
 
 if __name__ == '__main__':
index d4ed94e14db709a740a30f1b48931788dd84689a..2364970ba868406529d4a9b9e635c31d03270376 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QTextStream'''
 
@@ -19,8 +20,8 @@ class QTextStreamShiftTest(unittest.TestCase):
 
     def setUp(self):
         self.ba = QByteArray()
-        self.read = QTextStream(self.ba, QIODevice.ReadOnly)
-        self.write = QTextStream(self.ba, QIODevice.WriteOnly)
+        self.read = QTextStream(self.ba, QIODevice.OpenModeFlag.ReadOnly)
+        self.write = QTextStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
 
     def testNumber(self):
         '''QTextStream << number'''
@@ -83,7 +84,8 @@ class QTextStreamReadLinesFromDevice(unittest.TestCase):
         data.append((QByteArray(bytes('ole', "UTF-8")), ['ole']))
         data.append((QByteArray(bytes('ole\n', "UTF-8")), ['ole']))
         data.append((QByteArray(bytes('ole\r\n', "UTF-8")), ['ole']))
-        data.append((QByteArray(bytes('ole\r\ndole\r\ndoffen', "UTF-8")), ['ole', 'dole', 'doffen']))
+        data.append((QByteArray(bytes('ole\r\ndole\r\ndoffen', "UTF-8")),
+                    ['ole', 'dole', 'doffen']))
 
         self._check_data(data)
 
index a179c936f4bce02825e03bbbc951800c6da11fb4..055f04c35f43ea39d6d8efd153c2e66c10a1b4d9 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Producer-Consumer test/example with QThread'''
 
@@ -18,7 +19,7 @@ init_test_paths(False)
 
 logging.basicConfig(level=logging.WARNING)
 
-from PySide6.QtCore import QThread, QCoreApplication, QObject, SIGNAL
+from PySide6.QtCore import QThread, QCoreApplication, QObject
 
 
 class Bucket(QObject):
index 4e312dcc86448469a7285bd36884e843c9a2fcf0..93c83ea6eeedd91fd42e1eb452335ca126d01abc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for connecting signals between threads'''
 
index 4d26e0597b3e22783696ad9c17a2a1362736a1bf..bba19254ef883891b89f5d0fb4c0fd553b0eab88 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QThread'''
 
@@ -13,8 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QThread, QCoreApplication, QObject, QTimer, Slot
-from PySide6.QtCore import QEventLoop
+from PySide6.QtCore import QThread, QObject, QTimer, Slot
 
 from helper.usesqapplication import UsesQApplication
 
index 2ccaa300eb06d0409ee9e7077c8997bfc1964bbc..9b187104c253cbf794f2e1e8ecb9048dce9d72ff 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QTimer.singleShot'''
 
index 70f15198998a7bab6ba6ab2745e68a325e4dd7cc..813961f4cba58734c16e741528157877794415a1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for timeout() signals from QTimer object.'''
 
@@ -13,7 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject, QTimer, SIGNAL
+from PySide6.QtCore import QObject, QTimer
 from helper.usesqapplication import UsesQApplication
 
 
@@ -70,4 +71,3 @@ class TestTimeoutSignal(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index ad953f1cc29d8e89f60e8704197168e833a3e795..8fa36dda45d969e71c809876878af8f39d384d9a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -19,7 +20,8 @@ class TestQTimeZone (unittest.TestCase):
         timeZone = QTimeZone(id)
         self.assertTrue(timeZone.isValid())
         self.assertEqual(timeZone.id(), id)
-        name = timeZone.displayName(QTimeZone.GenericTime, QTimeZone.DefaultName)
+        name = timeZone.displayName(QTimeZone.TimeType.GenericTime,
+                                    QTimeZone.NameType.DefaultName)
         self.assertTrue(name)
 
 
index c08c727ec3d0a4cb5229072fc3839750e292ecf3..3e41a87b505ccb646c0feb2ad97a6cc603730b51 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test suite for QtCore.Qt namespace'''
 
index df16c74e03584f6d89b2b58fefd27e95ac6a2938..56cc0079ecc8d1bc0332b151515683e4a3e6afca 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test suite for QtCore.QUrl'''
 
@@ -40,15 +41,15 @@ class QUrlBasicConstructor(unittest.TestCase):
 
         url.setPath('/mail/view')
         self.assertEqual(url.toString(),
-                        'ftp://www.google.com:8080/mail/view')
+                         'ftp://www.google.com:8080/mail/view')
 
         url.setUserName('john')
         self.assertEqual(url.toString(),
-                        'ftp://john@www.google.com:8080/mail/view')
+                         'ftp://john@www.google.com:8080/mail/view')
 
         url.setPassword('abc123')
         self.assertEqual(url.toString(),
-                        'ftp://john:abc123@www.google.com:8080/mail/view')
+                         'ftp://john:abc123@www.google.com:8080/mail/view')
 
 
 class QueryItemsTest(unittest.TestCase):
@@ -92,7 +93,7 @@ class QueryItemsTest(unittest.TestCase):
         for i, data in enumerate(valid_data):
             url.addQueryItem(key, data)
             self.assertEqual(url.allQueryItemValues(key),
-                          list(valid_data[:i + 1]))
+                             list(valid_data[:i + 1]))
 
     def testPath(self):
         url = QUrl("http://qt-project.org/images/ban/pgs_front.jpg")
@@ -101,7 +102,7 @@ class QueryItemsTest(unittest.TestCase):
 # PYSIDE-345: No bindings for QUrlQuery
 
 
-class QueryItemsTest(unittest.TestCase):
+class QueryItemsTest2(unittest.TestCase):
     '''Test query item management'''
 
     def testQueryItems(self):
index 36eb29475544b95a2cc38178e72e57be8e38fe3c..fdb150fbac3b3528184663cf5dff29f0c6f449d8 100644 (file)
@@ -1,10 +1,10 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QUrlQuery'''
 
-import ctypes
 import os
 import sys
 import unittest
index 45c6fc89dbbb5e73867d457d7801a6e0d9993f98..c4a3f83116e0157aaec62227848ff580becacc19 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QUuid'''
 
index 23f6ba63919ebf42ab7d9362f9841fb7ab7f056c..db259646b30b9bbcc216c697df663777e11f3140 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QVersionNumber'''
 
index 87cceaa82387229210def2ab1971b53bd1861bda..084b69f87e720b91bd1cf8174ebb1ed4ad6c7dec 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-import PySide6
+# for 'self.original'
+import PySide6  # noqa
 from PySide6.QtCore import QByteArray, QDate, QDateTime, QTime, QLine, QLineF
 from PySide6.QtCore import Qt, QSize, QSizeF, QRect, QRectF, QPoint, QPointF
 try:
@@ -44,7 +46,7 @@ class QTimeReprCopy(ReprCopyHelper, unittest.TestCase):
 
 class QDateTimeReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
-        self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.LocalTime)
+        self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.TimeSpec.LocalTime)
 
 
 class QSizeReprCopy(ReprCopyHelper, unittest.TestCase):
index 0a0b03d40f2f708587abd0e84df374b25bea0584..1b66c856e697d4258a8dc9d569a970aa87e897d0 100644 (file)
@@ -4,4 +4,3 @@
         <file alias="sample.png">sample.png</file>
     </qresource>
 </RCC>
-
index 05766a22a808446aeeef0592533be53491716bda..36f596cc1aa8207509dd8f2e9a4215efcedfd818 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # Resource object code
 #
index de8f67bb3bd540e0c5e417143266311243b57aa2..2fda6640c91f715adec210c676fed17e8a57e37e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 2552591e5b15c0d655d57c6c7acd8fbd1b1f9006..522603bfa8a35d96572dc5151d89cfb14c0ed09f 100644 (file)
@@ -1,18 +1,20 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
+import gc
 import os
 import sys
 import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.usesqapplication import UsesQApplication
+from helper.usesqapplication import UsesQApplication  # noqa: E402
 
-from PySide6.QtCore import (QCoreApplication, QObject, QStringListModel,
+from PySide6.QtCore import (QCoreApplication, QObject, QStringListModel,  # noqa: E402
                             QTimer, Signal, Slot, Qt)
 
 
@@ -80,8 +82,11 @@ class TestConstructorConnection(UsesQApplication):
         model = QStringListModel(data_list,
                                  destroyed=destroyed_handler,
                                  dataChanged=changed_handler)
-        model.setData(model.index(0, 0), "bla", Qt.EditRole)
+        model.setData(model.index(0, 0), "bla", Qt.ItemDataRole.EditRole)
         del model
+        # PYSIDE-535: Need to collect garbage twice in PyPy to trigger deletion
+        gc.collect()
+        gc.collect()
 
         self.assertTrue(was_changed)
         self.assertTrue(was_destroyed)
index 82087ab9a38bacd1db7547287da8be664e7a7978..7310a1b6216970c29d50775a1697418c9eb41cd4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import sys
@@ -7,7 +8,7 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
 init_test_paths(False)
 
 from PySide6.QtCore import Property, QSize
@@ -15,7 +16,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow, QWidget
 
 is_pypy = hasattr(sys, "pypy_version_info")
 if not is_pypy:
-    from PySide6.support import feature
+    from PySide6.support import feature  # noqa
 
 """
 snake_prop_feature_test.py
@@ -35,19 +36,19 @@ class Window(QWidget):
 @unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
 class FeatureTest(unittest.TestCase):
     def setUp(self):
-        qApp or QApplication()
+        qApp or QApplication()  # noqa
         feature.reset()
 
     def tearDown(self):
         feature.reset()
-        qApp.shutdown()
+        qApp.shutdown()  # noqa
 
     def testRenamedFunctions(self):
         window = Window()
         window.setWindowTitle('camelCase')
 
         # and now the same with snake_case enabled
-        from __feature__ import snake_case
+        from __feature__ import snake_case  # noqa
 
         # Works with the same window! window = Window()
         window.set_window_title('snake_case')
@@ -59,9 +60,9 @@ class FeatureTest(unittest.TestCase):
         with self.assertRaises(AttributeError):
             window.modal
 
-        from __feature__ import snake_case, true_property
+        from __feature__ import snake_case, true_property  # noqa
         # PYSIDE-1548: Make sure that another import does not clear the features.
-        import sys
+        import sys  # noqa
 
         self.assertTrue(isinstance(QWidget.modal, property))
         self.assertTrue(isinstance(window.modal, bool))
@@ -76,19 +77,19 @@ class FeatureTest(unittest.TestCase):
             window.modal
 
     def testClassProperty(self):
-        from __feature__ import snake_case, true_property
+        from __feature__ import snake_case, true_property  # noqa
         # We check the class...
         self.assertEqual(type(QApplication.quit_on_last_window_closed), bool)
         x = QApplication.quit_on_last_window_closed
         QApplication.quit_on_last_window_closed = not x
         self.assertEqual(QApplication.quit_on_last_window_closed, not x)
         # ... and now the instance.
-        self.assertEqual(type(qApp.quit_on_last_window_closed), bool)
-        x = qApp.quit_on_last_window_closed
-        qApp.quit_on_last_window_closed = not x
-        self.assertEqual(qApp.quit_on_last_window_closed, not x)
+        self.assertEqual(type(qApp.quit_on_last_window_closed), bool)  # noqa
+        x = qApp.quit_on_last_window_closed  # noqa
+        qApp.quit_on_last_window_closed = not x  # noqa
+        self.assertEqual(qApp.quit_on_last_window_closed, not x)  # noqa
         # make sure values are equal
-        self.assertEqual(qApp.quit_on_last_window_closed,
+        self.assertEqual(qApp.quit_on_last_window_closed,  # noqa
                          QApplication.quit_on_last_window_closed)
 
     def testUserClassNotAffected(self):
@@ -111,7 +112,7 @@ class FeatureTest(unittest.TestCase):
         self.assertTrue(isinstance(UserClass.someFunc2, FunctionType))
         self.assertTrue(isinstance(UserClass.addAction, MethodDescriptorType))
 
-        from __feature__ import snake_case
+        from __feature__ import snake_case  # noqa
 
         inspect = UserClass.__dict__
         self.assertTrue(isinstance(inspect["someFunc1"], FunctionType))
@@ -120,7 +121,7 @@ class FeatureTest(unittest.TestCase):
         self.assertTrue(isinstance(UserClass.add_action, MethodDescriptorType))
 
     def testTrueProperyCanOverride(self):
-        from __feature__ import true_property
+        from __feature__ import true_property  # noqa
 
         class CustomWidget(QWidget):
             global prop_result
index 223c334d3212f2ac744b1e51a8c2b2d702449f4e..0fd207b41f0056afb216f6fcc5bbf0b83de62986 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index bbf96e44aaf6f03732bef291725f37f0e09a8ddc..1616631cf61476016839bc953a7c445378f0833f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for overloads involving static and non-static versions of a method.'''
 
@@ -62,4 +63,3 @@ class StaticNonStaticMethodsTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index a453d1ac150a73b3f3bac8e03673ef08c28d2086..ffef4cda07189c3593652e93a90d620bdaa47953 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test case for QObject.signalsBlocked() and blockSignal()'''
 
index a3b2e257f30838bd0c9a270a2eb9ce5cabe805f4..ffa47cd532014f8e8f5ffed253700c50c6583aa0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -63,4 +64,3 @@ class QtTrNoopTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index da09e47b6255952862278bd016c7f96a0bbb9f54..be6145a2700e5d59960cfcd1f18f427713119a16 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests to test QTranslator and translation in general.'''
 
@@ -99,4 +100,3 @@ class TranslationTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 7b38685c8d25e3d82f206939d1d60ede8240dea0..5d443e7cad385e93b2343e0cfcf8a0ff75c15d12 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests the presence of unary operator __neg__ on the QPoint class'''
 
@@ -41,7 +42,7 @@ class NegUnaryOperatorTest(unittest.TestCase):
         __neg__method_exists = True
         try:
             neg_qpoint = -self.qpoint
-        except:
+        except:  # noqa: E722
             __neg__method_exists = False
 
         self.assertTrue(__neg__method_exists)
@@ -50,4 +51,3 @@ class NegUnaryOperatorTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 73ce96c77ec1676485f64d8b6dc8710f0c3f4e53..89cd31f4ae65fc2b27e0c3166e078459775a1010 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QString conversion to/from Python Unicode'''
 
@@ -14,7 +15,6 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QObject
-from PySide6.QtCore import QByteArray
 
 
 class UnicodeConversion(unittest.TestCase):
@@ -41,4 +41,3 @@ class UnicodeConversion(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 63414378a57af7bf6be187bc1ed275aba3505532..a12484e8ab5e01d941d47d960724755c13ea30b9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -21,7 +22,7 @@ class TestVersionInfo(unittest.TestCase):
         self.assertEqual(len(v), 5)
         self.assertEqual(type(v[0]), int)
         self.assertEqual(type(v[1]), int)
-        self.assertEqual(type(v[2]), int)
+        #self.assertEqual(type(v[2]), int)
         self.assertEqual(type(v[3]), str)
         self.assertEqual(type(v[4]), str)
 
index a37db94217a5d71d3fa345d0f0cf1123c7587655..e045e91d9cc20f0db47c56bee9f9e5d7e935ac74 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QDBus'''
 
@@ -15,9 +16,7 @@ init_test_paths(False)
 from helper.usesqapplication import UsesQApplication
 
 import sys
-from PySide6.QtCore import QCoreApplication
-from PySide6.QtDBus import (QDBusConnection, QDBusConnectionInterface,
-                            QDBusInterface, QDBusReply)
+from PySide6.QtDBus import QDBusConnection
 
 
 def service_names():
index 98ada6d47fd026414ec1dcd95a7cb8b7a3744dda..bfa497eb4e3ebd681bcec418b1cb559dc24865a0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtDataVisualization'''
 
index 2f7cb08b98b3a3f5f2230e7663daf3c24f8e6809..0b153a6e29036ba4bc18ce3a0ec302b343b414cd 100644 (file)
@@ -1 +1 @@
-# Please add some tests, here
+PYSIDE_TEST(qgraphs_numpy_test.py)
diff --git a/sources/pyside6/tests/QtGraphs/QtGraphs.pyproject b/sources/pyside6/tests/QtGraphs/QtGraphs.pyproject
new file mode 100644 (file)
index 0000000..b15b05b
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "files": ["qgraphs_numpy_test.py"]
+}
diff --git a/sources/pyside6/tests/QtGraphs/qgraphs_numpy_test.py b/sources/pyside6/tests/QtGraphs/qgraphs_numpy_test.py
new file mode 100644 (file)
index 0000000..0b3419b
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+'''Test cases for QGraphs/numpy'''
+
+import os
+import sys
+import unittest
+try:
+    import numpy as np
+    HAVE_NUMPY = True
+except ModuleNotFoundError:
+    HAVE_NUMPY = False
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from helper.usesqapplication import UsesQApplication
+from PySide6.QtGraphs import QLineSeries
+
+
+@unittest.skipUnless(HAVE_NUMPY, "requires numpy")
+class QGraphsNumpyTestCase(UsesQApplication):
+    '''Tests related to QGraphs/numpy'''
+
+    def test(self):
+        """PYSIDE-2313: Verify various types."""
+        line_series = QLineSeries()
+        data_types = [np.short, np.ushort, np.int32, np.uint32,
+                      np.int64, np.uint64, np.float32, np.float64]
+        for dt in data_types:
+            print("Testing ", dt)
+            old_size = line_series.count()
+            x_arr = np.array([2], dtype=dt)
+            y_arr = np.array([3], dtype=dt)
+            line_series.appendNp(x_arr, y_arr)
+            size = line_series.count()
+            self.assertEqual(size, old_size + 1)
+            point = line_series.points()[size - 1]
+            self.assertEqual(point.x(), 2)
+            self.assertEqual(point.y(), 3)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/sources/pyside6/tests/QtGraphsWidgets/CMakeLists.txt b/sources/pyside6/tests/QtGraphsWidgets/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2f7cb08
--- /dev/null
@@ -0,0 +1 @@
+# Please add some tests, here
index 9b9f35807fa8ea47ebea46829433519a8ffede06..b1e2f0b09d6ebded297367a5cc96bcd88e17d11d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' unit test for BUG #1091 '''
 
index 771fa5a4b4a603eae6cd50cf63450e86027d8ccb..1b447be72dbaaadaddf531c8d6eec0945a742a11 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 367: http://bugs.openbossa.org/show_bug.cgi?id=367'''
 
index ba8b67bbb7fd7bc7b4fa5c5b22d486088d5e9cf2..9f94042ad5057055f44d82961b83a16199f60633 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -19,10 +20,10 @@ class TestBug493(unittest.TestCase):
     def testIt(self):
         # We need a qapp otherwise Qt will crash when trying to detect the
         # current platform
-        app = QGuiApplication([])
-        ev1 = QKeyEvent(QEvent.KeyRelease, Qt.Key_Delete, Qt.NoModifier)
-        ev2 = QKeyEvent(QEvent.KeyRelease, Qt.Key_Copy, Qt.NoModifier)
-        ks = QKeySequence.Delete
+        app = QGuiApplication([])  # noqa: F841
+        ev1 = QKeyEvent(QEvent.Type.KeyRelease, Qt.Key.Key_Delete, Qt.KeyboardModifier.NoModifier)
+        ev2 = QKeyEvent(QEvent.Type.KeyRelease, Qt.Key.Key_Copy, Qt.KeyboardModifier.NoModifier)
+        ks = QKeySequence.StandardKey.Delete
 
         self.assertTrue(ev1.matches(ks))
         self.assertFalse(ev2.matches(ks))
index 80b79f640a8d953cc1abfb9d21ca6ee398c0c649..332c207c6711145b437677afa631f8824a992ad0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index d90d1e7546a7c8c80b3b1f2ee43a6fa8720f4e0b..505e068dc84626b21cac1db4d1e49063fc598f92 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -23,7 +24,7 @@ class Bug617(unittest.TestCase):
     def testRepr(self):
         c = QColor.fromRgb(1, 2, 3, 4)
         s = c.spec()
-        self.assertEqual(repr(s), repr(QColor.Rgb))
+        self.assertEqual(repr(s), repr(QColor.Spec.Rgb))
 
     def testOutOfBounds(self):
         e = MyEvent()
index 17d6c3b774a6d3ba82667981f29aee06d2830f5c..77b5d525164be6523e5c8aef5aa9087ab43b4ee5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 4ab8e9f1937e64618b55d16f7c9660bf0feae9f9..e45b40596b402de176498db37335c89ab1f406c5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 90d78ea89a9f497267f33cc8034a722aa07610e8..d9d0654ac4b816a1553bebd607e14312da5f26c9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -16,5 +17,4 @@ if __name__ == '__main__':
     stringListModel = QStringListModel(['one', 'two'])
     idx = stringListModel.index(1, 0)
     persistentModelIndex = QPersistentModelIndex(idx)
-    stringListModel.data(persistentModelIndex, Qt.DisplayRole)
-
+    stringListModel.data(persistentModelIndex, Qt.ItemDataRole.DisplayRole)
index ece314cc8493656d40b173209fbe3ef5304d4da7..e5b80cbf8bdb46220fc711888b4b2162766b10c3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,8 +18,9 @@ from PySide6.QtGui import QBitmap, QImage
 
 class TestQBitmap(UsesQApplication):
     def testFromDataMethod(self):
-        dataBits = bytes('\x38\x28\x38\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\xfe\xfe\x7c\x7c\x38\x38\x10\x10', "UTF-8")
-        bim = QBitmap.fromData(QSize(8, 48), dataBits, QImage.Format_Mono)  # missing function
+        dataBits = bytes('\x38\x28\x38\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\xfe\xfe\x7c\x7c\x38\x38\x10\x10', "UTF-8")  # noqa: E501
+        # missing function
+        bim = QBitmap.fromData(QSize(8, 48), dataBits, QImage.Format.Format_Mono)  # noqa: F841
 
 
 if __name__ == '__main__':
index a870e4bf1200813daff0c6cf3d1c09e32a63f530..127981f1faa7d3356d91c2fdc00456ac2a5179bb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 04425f0630793e6beeef1976f25575d7a6ec0084..a218398a91e490adb978175c5b776cb0f25a3bc3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index e74adc198263f6291cc32aebe13de42412bfa0bb..2bac8f5426ab94e95b779a003110601be3d53880 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 6519eef616152fa7d9f5b00d37dfa35c7bf916ab..bd5ab4ba0b3aade09910be890a966402b0483f75 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for PYSIDE-344, imul/idiv are used instead of mul/div, modifying the argument passed in'''
 
index 9a13eb485f1f8a98eebb47be8d3da41366222b2c..4276181ad48be2c82408b690fda5c82ea0552923 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 from copy import deepcopy
 import os
@@ -96,45 +97,50 @@ class QPolygonDeepCopy(DeepCopyHelper, unittest.TestCase):
         self.original = QPolygon([QPoint(1, 2), QPoint(3, 4), QPoint(5, 6)])
 
 
-# Avoid these tests until get gcc fixed
-# Related bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43247
-"""
 class QMatrix2x2DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix2x2([1, 2, 3, 4])
 
+
 class QMatrix2x3DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix2x3([1, 2, 3, 4, 5, 6])
 
+
 class QMatrix2x4DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix2x4([1, 2, 3, 4, 5, 6, 7, 8])
 
+
 class QMatrix3x2DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix3x2([1, 2, 3, 4, 5, 6])
 
+
 class QMatrix3x3DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix3x3([1, 2, 3, 4, 5, 6, 7, 8, 9])
 
+
 class QMatrix3x4DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix3x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
 
+
 class QMatrix4x2DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix4x2([1, 2, 3, 4, 5, 6, 7, 8])
 
+
 class QMatrix4x3DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix4x3([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
 
+
 class QMatrix4x4DeepCopy(DeepCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix4x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
-"""
+
 
 if __name__ == '__main__':
     unittest.main()
index 01d8fbc026c4ab3d813e41b24d31e6f13c14bc7b..57f137fdfcc644a79d91080223bf08245cb5d4af 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,7 +18,7 @@ from PySide6.QtGui import QWindow
 
 class MyFilter(QObject):
     def eventFilter(self, obj, event):
-        if event.type() == QEvent.KeyPress:
+        if event.type() == QEvent.Type.KeyPress:
             pass
         return QObject.eventFilter(self, obj, event)
 
index ba438f8e0a48b036d6c05af21d4120585add70fa..0ed7fe40205dbd05ff5aff08eeff33202478f85d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QImage'''
 
@@ -25,7 +26,7 @@ class SetPixelFloat(UsesQApplication):
         # Acquire resources
         super(SetPixelFloat, self).setUp()
         self.color = qRgb(255, 0, 0)
-        self.image = QImage(200, 200, QImage.Format_RGB32)
+        self.image = QImage(200, 200, QImage.Format.Format_RGB32)
 
     def tearDown(self):
         # Release resources
index 1ac65f7092015350561e3e9daee07d4101aac5fe..83f4ac0c647626e294a7acd67e1945b0f0699267 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import importlib
 import importlib.util
@@ -59,5 +60,3 @@ class TestModuleReloading(unittest.TestCase):
 
 if __name__ == "__main__":
     unittest.main()
-
-
index 800e6f0728d04b901f6b1ecfc490e095d64396c7..15c575d30758370a5a5237ee7fcd00e799f73893 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QBrush'''
 
@@ -13,7 +14,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import Qt
-from PySide6.QtGui import QColor, QBrush
+from PySide6.QtGui import QColor, QBrush, QConicalGradient
 
 from helper.usesqapplication import UsesQApplication
 
@@ -27,8 +28,16 @@ class Constructor(UsesQApplication):
         obj = QBrush(color)
         self.assertEqual(obj.color(), color)
 
-        obj = QBrush(Qt.blue)
-        self.assertEqual(obj.color(), Qt.blue)
+        obj = QBrush(Qt.GlobalColor.blue)
+        self.assertEqual(obj.color(), Qt.GlobalColor.blue)
+
+    def testGradient(self):
+        """Test type discovery on class hierarchies with non-virtual
+           destructors by specifying a polymorphic-id-expression without
+           polymorphic-name-function."""
+        gradient = QConicalGradient()
+        brush = QBrush(gradient)
+        self.assertEqual(type(brush.gradient()), type(gradient))
 
 
 if __name__ == '__main__':
index 609951be6de5cb32f1eb7ce8ff0a1618cbe35f8d..a99ad6458c39c0f9a10d690891ba82ce0dc49647 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import pickle
index bbd558f10e616511bdf5acec811e0293de493631..b1576b6f30b28a08290e068113c58933b0d648a0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import colorsys
 import gc
@@ -12,7 +13,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-import PySide6
+# For c.__repr__
+import PySide6  # noqa
 from PySide6.QtCore import Qt
 from PySide6.QtGui import QColor, QColorConstants
 
@@ -40,7 +42,8 @@ class QColorGetTest(unittest.TestCase):
         self.assertEqual(self.color.getCmyk(), (170, 85, 0, 195, 80))
 
     def testGetCmykF(self):  # not supported by colorsys
-        for x, y in zip(self.color.getCmykF(), (170 / 255.0, 85 / 255.0, 0, 195 / 255.0, 80 / 255.0)):
+        for x, y in zip(self.color.getCmykF(),
+                        (170 / 255.0, 85 / 255.0, 0, 195 / 255.0, 80 / 255.0)):
             self.assertTrue(x - y < 1 / 10000.0)
 
 
@@ -61,7 +64,7 @@ class QColorEqualGlobalColor(unittest.TestCase):
 
     def testEqualGlobalColor(self):
         '''QColor == Qt::GlobalColor'''
-        self.assertEqual(QColor(255, 0, 0), Qt.red)
+        self.assertEqual(QColor(255, 0, 0), Qt.GlobalColor.red)
 
 
 class QColorCopy(unittest.TestCase):
index 1494fe370754af5893483af7cb7643f435fdcd7f..b0ee43fbff8847a0bdf65c8f84bf6c4a2651d5f2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test for Bug 630 - Fails to resolve overload for QCursor(QBitmap, QBitmap, int, int)
 http://bugs.openbossa.org/show_bug.cgi?id=630
@@ -14,16 +15,15 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtGui import QBitmap, QCursor, QPixmap
+from PySide6.QtGui import QBitmap, QCursor
 from helper.usesqapplication import UsesQApplication
 
 
 class TestQCursor(UsesQApplication):
     def testQCursorConstructor(self):
         bmp = QBitmap(16, 16)
-        cursor = QCursor(bmp, bmp, 16, 16)
+        cursor = QCursor(bmp, bmp, 16, 16)  # noqa
 
 
 if __name__ == '__main__':
     unittest.main()
-
index f2c86abf749d206715269011949c24a3bafcbb3b..9fea562031b24c5b954a98a27793492ca5ba91c3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -24,11 +25,11 @@ class QPixmapQDatastream(UsesQApplication):
         self.source_pixmap = QPixmap(100, 100)
         # PYSIDE-1533: Use Qt.transparent to force Format_ARGB32_Premultiplied
         # when converting to QImage in any case.
-        self.source_pixmap.fill(Qt.transparent)
+        self.source_pixmap.fill(Qt.GlobalColor.transparent)
         self.output_pixmap = QPixmap()
         self.buffer = QByteArray()
-        self.read_stream = QDataStream(self.buffer, QIODevice.ReadOnly)
-        self.write_stream = QDataStream(self.buffer, QIODevice.WriteOnly)
+        self.read_stream = QDataStream(self.buffer, QIODevice.OpenModeFlag.ReadOnly)
+        self.write_stream = QDataStream(self.buffer, QIODevice.OpenModeFlag.WriteOnly)
 
     def testStream(self):
         self.write_stream << self.source_pixmap
@@ -37,7 +38,7 @@ class QPixmapQDatastream(UsesQApplication):
 
         image = self.output_pixmap.toImage()
         pixel = image.pixel(10, 10)
-        self.assertEqual(pixel, QColor(Qt.transparent).rgba())
+        self.assertEqual(pixel, QColor(Qt.GlobalColor.transparent).rgba())
         self.assertEqual(self.source_pixmap.toImage(), image)
 
 
index 8d578152dd10b04b40b09d7916b2bc1c180fa9a3..668a16a802c7fd094c9644575c221ca22b327e67 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QDesktopServices'''
 
index f81d47ec6b91c3b8900d915c8529afa09622f5c5..1971cc555ece2d517a7f3202a3e08a684d53c2e7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 98c4c8f9697ea49ae360cf605cbcc747b3b33873..0de7fe8306a391315b06d468cc5fefc9ad910348 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for inject codes and modifications on QFontMetrics
    and QFontMetricsF'''
@@ -41,49 +42,52 @@ class BoundingRectTest(QFontMetricsTest):
     def testIntDefault(self):
         '''QFontMetrics.boundingRect(int, int, int, int, ...) - default args'''
         rect = self.metrics.boundingRect(0, 0, 0, 0,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT')
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company')
         self.assertTrue(isinstance(rect, QRect))
 
     def testIntWithArg(self):
         '''QFontMetrics.boundingRect(int, int, int, int, ...) - single arg'''
         rect = self.metrics.boundingRect(0, 0, 0, 0,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 2)
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company', 2)
         self.assertTrue(isinstance(rect, QRect))
 
     def testIntWithFull(self):
         '''QFontMetrics.boundingRect(int, int, int, int, ...) - all argss'''
         rect = self.metrics.boundingRect(0, 0, 0, 0,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20, [1, 2, 3, 4, 5])
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company', 20, [1, 2, 3, 4, 5])
         self.assertTrue(isinstance(rect, QRect))
 
     def testIntTypeError(self):
         '''QFontMetrics.boundingRect(int, int, int, int, ...) - type error'''
         self.assertRaises(TypeError, self.metrics.boundingRect, 0, 0, 0, 0,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20, ['aaaa', 'ase'])
+                          Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                          'PySide by Qt Company', 20, ['aaaa', 'ase'])
 
     def testQRectDefault(self):
         '''QFontMetrics.boundingRect(QRect, ...) - default args'''
         arg = QRect(0, 0, 100, 200)
-        rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT')
+        rect = self.metrics.boundingRect(arg,
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company')
         self.assertTrue(isinstance(rect, QRect))
 
     def testQRectWithArg(self):
         '''QFontMetrics.boundingRect(QRect, ...) - only tabstops'''
         arg = QRect(0, 0, 100, 200)
-        rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 2)
+        rect = self.metrics.boundingRect(arg,
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company', 2)
         self.assertTrue(isinstance(rect, QRect))
 
     def testQRectWithFull(self):
         '''QFontMetrics.boundingRect(QRect, ...) - all arguments'''
         arg = QRect(0, 0, 100, 200)
-        rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20,
+        rect = self.metrics.boundingRect(arg,
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company', 20,
                                          [1, 2, 3, 4, 5])
         self.assertTrue(isinstance(rect, QRect))
 
@@ -91,8 +95,8 @@ class BoundingRectTest(QFontMetricsTest):
         '''QFontMetrics.boundingRect(QRect, ...) - type error'''
         arg = QRect(0, 0, 100, 200)
         self.assertRaises(TypeError, self.metrics.boundingRect, arg,
-                          Qt.TextExpandTabs | Qt.AlignLeft,
-                          'PySide by INdT', 20, ['aaaa', 'ase'])
+                          Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                          'PySide by Qt Company', 20, ['aaaa', 'ase'])
 
 
 class SizeTest(QFontMetricsTest):
@@ -100,27 +104,27 @@ class SizeTest(QFontMetricsTest):
 
     def testDefault(self):
         '''QFontMetrics.size - default arguments'''
-        size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
-                                 'PySide by INdT')
+        size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+                                 'PySide by Qt Company')
         self.assertTrue(isinstance(size, QSize))
 
     def testWithTabStops(self):
         '''QFontMetrics.size - only tabstops'''
-        size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
-                                 'PySide by INdT', 2)
+        size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+                                 'PySide by Qt Company', 2)
         self.assertTrue(isinstance(size, QSize))
 
     def testFull(self):
         '''QFontMetrics.size - all arguments'''
-        size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
-                                 'PySide by INdT', 2, [1, 2, 3, 4])
+        size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+                                 'PySide by Qt Company', 2, [1, 2, 3, 4])
         self.assertTrue(isinstance(size, QSize))
 
     def testTypeError(self):
         '''QFontMetrics.size - type error'''
         self.assertRaises(TypeError, self.metrics.size,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20, ['aaaa', 'ase'])
+                          Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                          'PySide by Qt Company', 20, ['aaaa', 'ase'])
 
 
 class QFontMetricsFTest(UsesQApplication):
@@ -145,22 +149,25 @@ class FBoundingRectTest(QFontMetricsFTest):
     def testQRectDefault(self):
         '''QFontMetricsF.boundingRect(QRectF, ...) - default args'''
         arg = QRectF(0, 0, 100, 200)
-        rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT')
+        rect = self.metrics.boundingRect(arg,
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company')
         self.assertTrue(isinstance(rect, QRectF))
 
     def testQRectWithArg(self):
         '''QFontMetricsF.boundingRect(QRectF, ...) - only tabstops'''
         arg = QRectF(0, 0, 100, 200)
-        rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 2)
+        rect = self.metrics.boundingRect(arg,
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company', 2)
         self.assertTrue(isinstance(rect, QRectF))
 
     def testQRectWithFull(self):
         '''QFontMetricsF.boundingRect(QRectF, ...) - all arguments'''
         arg = QRectF(0, 0, 100, 200)
-        rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20,
+        rect = self.metrics.boundingRect(arg,
+                                         Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                                         'PySide by Qt Company', 20,
                                          [1, 2, 3, 4, 5])
         self.assertTrue(isinstance(rect, QRectF))
 
@@ -168,8 +175,8 @@ class FBoundingRectTest(QFontMetricsFTest):
         '''QFontMetricsF.boundingRect(QRectF, ...) - type error'''
         arg = QRectF(0, 0, 100, 200)
         self.assertRaises(TypeError, self.metrics.boundingRect, arg,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20, ['aaaa', 'ase'])
+                          Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                          'PySide by Qt Company', 20, ['aaaa', 'ase'])
 
 
 class FSizeTest(QFontMetricsFTest):
@@ -177,27 +184,27 @@ class FSizeTest(QFontMetricsFTest):
 
     def testDefault(self):
         '''QFontMetricsF.size - default arguments'''
-        size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
-                                 'PySide by INdT')
+        size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+                                 'PySide by Qt Company')
         self.assertTrue(isinstance(size, QSizeF))
 
     def testWithTabStops(self):
         '''QFontMetricsF.size - only tabstops'''
-        size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
-                                 'PySide by INdT', 2)
+        size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+                                 'PySide by Qt Company', 2)
         self.assertTrue(isinstance(size, QSizeF))
 
     def testFull(self):
         '''QFontMetricsF.size - all arguments'''
-        size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
-                                 'PySide by INdT', 2, [1, 2, 3, 4])
+        size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+                                 'PySide by Qt Company', 2, [1, 2, 3, 4])
         self.assertTrue(isinstance(size, QSizeF))
 
     def testTypeError(self):
         '''QFontMetricsF.size - type error'''
         self.assertRaises(TypeError, self.metrics.size,
-                          Qt.TextExpandTabs | Qt.AlignLeft,
-                          'PySide by INdT', 20, ['aaaa', 'ase'])
+                          Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+                          'PySide by Qt Company', 20, ['aaaa', 'ase'])
 
 
 class QCharTest(QFontMetricsFTest):
index a3d0942aafa576a627f161e6e11505794ebff994..4112cd695202ea56ee3676d009ab25550b60cc47 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 18ef3d815e3d4659cd1ad937041c35dc1e0dcc92..ac83ee297c9e20db4770bf072c8c2f51692546d2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 5912bf318d4b128ffa6ef5c0d0da46c8de2e39d3..61fffc12486c271ffb18504eeed6cdda90238e9f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QImage'''
 
@@ -34,16 +35,16 @@ class QImageTest(UsesQApplication):
         img2.setColorSpace(img0.colorSpace())
         self.assertEqual(img0, img2)
 
-        ## test scanLine method
+        # test scanLine method
         data1 = img0.scanLine(0)
         data2 = img1.scanLine(0)
         self.assertEqual(data1, data2)
 
     def testEmptyBuffer(self):
-        img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format_ARGB32)
+        img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format.Format_ARGB32)  # noqa: F841
 
     def testEmptyStringAsBuffer(self):
-        img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format_ARGB32)
+        img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format.Format_ARGB32)  # noqa: F841
 
     def testXpmConstructor(self):
         img = QImage(xpm)
index 2d9cb96f1100efb1cfde4afdb79dcae481b989f6..e7a09a72a77dc80ca6cbd04b3d562955aa5b7e90 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QImage/Windows'''
 
@@ -18,8 +19,8 @@ from helper.usesqapplication import UsesQApplication
 
 
 def create_image():
-    result = QImage(20, 20, QImage.Format_RGB32)
-    result.fill(Qt.white)
+    result = QImage(20, 20, QImage.Format.Format_RGB32)
+    result.fill(Qt.GlobalColor.white)
     return result
 
 
index 179bff7459594460e879e81a44d0bf833b51e8e7..4b45cdc6d3d12373a0017a1183ee862f1a9ae783 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -28,4 +29,3 @@ class QItemSelectionTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 3d72fb50ac2f6286fc62489054557376dee83291..56e3a00ffdca0106fdaae7b125f0959eecdf5d0c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -22,11 +23,12 @@ class QKeySequenceTest(UsesQApplication):
         # bug #774
         # PYSIDE-1735: Remapped from Qt.Modifier to Qt.KeyboardModifier
         #              Note that Qt.(Keyboard)?Modifier will be no longer IntFlag.
-        ks = QKeySequence(Qt.ShiftModifier, Qt.ControlModifier, Qt.Key_P, Qt.Key_R)
-        self.assertEqual(ks[0].keyboardModifiers(), Qt.ShiftModifier)
-        self.assertEqual(ks[1].keyboardModifiers(), Qt.ControlModifier)
-        self.assertEqual(ks[2].key(), Qt.Key_P)
-        self.assertEqual(ks[3].key(), Qt.Key_R)
+        ks = QKeySequence(Qt.KeyboardModifier.ShiftModifier, Qt.KeyboardModifier.ControlModifier,
+                          Qt.Key.Key_P, Qt.Key.Key_R)
+        self.assertEqual(ks[0].keyboardModifiers(), Qt.KeyboardModifier.ShiftModifier)
+        self.assertEqual(ks[1].keyboardModifiers(), Qt.KeyboardModifier.ControlModifier)
+        self.assertEqual(ks[2].key(), Qt.Key.Key_P)
+        self.assertEqual(ks[3].key(), Qt.Key.Key_R)
 
         def testAutoMnemonic(self):
             qt_set_sequence_auto_mnemonic(True)
index 103b91ad69d75a363eab3cc63bb1554326cf5e6b..c304b83e8a62bdca3684ab604c4b8c15b30bee6b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -26,7 +27,7 @@ except ModuleNotFoundError:
 class QPainterDrawText(UsesQApplication):
     def setUp(self):
         super(QPainterDrawText, self).setUp()
-        self.image = QImage(32, 32, QImage.Format_ARGB32)
+        self.image = QImage(32, 32, QImage.Format.Format_ARGB32)
         self.painter = QPainter(self.image)
         self.text = 'teste!'
 
@@ -41,14 +42,15 @@ class QPainterDrawText(UsesQApplication):
     def testDrawText(self):
         # bug #254
         rect = self.painter.drawText(100, 100, 100, 100,
-                                     Qt.AlignCenter | Qt.TextWordWrap,
+                                     Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap,
                                      self.text)
         self.assertTrue(isinstance(rect, QRect))
 
     def testDrawTextWithRect(self):
         # bug #225
         rect = QRect(100, 100, 100, 100)
-        newRect = self.painter.drawText(rect, Qt.AlignCenter | Qt.TextWordWrap,
+        newRect = self.painter.drawText(rect,
+                                        Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap,
                                         self.text)
 
         self.assertTrue(isinstance(newRect, QRect))
@@ -56,7 +58,8 @@ class QPainterDrawText(UsesQApplication):
     def testDrawTextWithRectF(self):
         '''QPainter.drawText(QRectF, ... ,QRectF*) inject code'''
         rect = QRectF(100, 52.3, 100, 100)
-        newRect = self.painter.drawText(rect, Qt.AlignCenter | Qt.TextWordWrap,
+        newRect = self.painter.drawText(rect,
+                                        Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap,
                                         self.text)
 
         self.assertTrue(isinstance(newRect, QRectF))
@@ -104,7 +107,7 @@ class SetBrushWithOtherArgs(UsesQApplication):
     '''Using qpainter.setBrush with args other than QBrush'''
 
     def testSetBrushGradient(self):
-        image = QImage(32, 32, QImage.Format_ARGB32)
+        image = QImage(32, 32, QImage.Format.Format_ARGB32)
         with QPainter(image) as painter:
             gradient = QLinearGradient(0, 0, 0, 0)
             painter.setBrush(gradient)
@@ -112,4 +115,3 @@ class SetBrushWithOtherArgs(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 2dca2aca2fcd357c415f376bc9033daf151a4151..2b1549384ddc6cccd3fea1b939368255ab5ae324 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -21,7 +22,9 @@ class QPdfWriterTest(UsesQApplication):
         temporaryFile = QTemporaryFile(QDir.tempPath() + "/pdfwriter_test_XXXXXX.pdf")
         self.assertTrue(temporaryFile.open())
         pdfWriter = QPdfWriter(temporaryFile)
-        pdfWriter.setPageLayout(QPageLayout(QPageSize(QPageSize.A4), QPageLayout.Portrait, QMarginsF(10, 10, 10, 10)))
+        pdfWriter.setPageLayout(QPageLayout(QPageSize(QPageSize.PageSizeId.A4),
+                                QPageLayout.Orientation.Portrait,
+                                QMarginsF(10, 10, 10, 10)))
         doc = QTextDocument("Some text")
         doc.print_(pdfWriter)
         temporaryFile.close()
index 7e8604606623cf4b122a84e2e350673c5a16893c..1a187c1ed7c98a9562296166b0f1f43aad5058e3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -24,9 +25,9 @@ class Painting(QRasterWindow):
 
     def paintEvent(self, event):
         with QPainter(self) as painter:
-            painter.setPen(Qt.NoPen)
+            painter.setPen(Qt.PenStyle.NoPen)
             self.penFromEnum = painter.pen()
-            intVal = Qt.NoPen.value
+            intVal = Qt.PenStyle.NoPen.value
             painter.setPen(intVal)
             self.penFromInteger = painter.pen()
         QTimer.singleShot(20, self.close)
@@ -40,7 +41,7 @@ class QPenTest(UsesQApplication):
         style = Qt.PenStyle(0)
         cap = Qt.PenCapStyle(0)
         join = Qt.PenJoinStyle(0)
-        pen = QPen(Qt.blue, width, style, cap, join)
+        pen = QPen(Qt.GlobalColor.blue, width, style, cap, join)  # noqa: F841
 
     def testSetPenWithPenStyleEnum(self):
         '''Calls QPainter.setPen with both enum and integer. Bug #511.'''
@@ -48,10 +49,9 @@ class QPenTest(UsesQApplication):
         w.show()
         w.setTitle("qpen_test")
         self.app.exec()
-        self.assertEqual(w.penFromEnum.style(), Qt.NoPen)
-        self.assertEqual(w.penFromInteger.style(), Qt.SolidLine)
+        self.assertEqual(w.penFromEnum.style(), Qt.PenStyle.NoPen)
+        self.assertEqual(w.penFromInteger.style(), Qt.PenStyle.SolidLine)
 
 
 if __name__ == '__main__':
     unittest.main()
-
index 869d159526699e79777565863fb9a40aec3273be..f9239b25581293b9052ccffd2912e11569d4bc1e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit test for QPixelFormat'''
 
@@ -19,8 +20,8 @@ from PySide6.QtGui import QColor, QImage, QPixelFormat, qPixelFormatRgba
 
 class QPixelFormatTest(UsesQApplication):
     def test(self):
-        image = QImage(QSize(200, 200), QImage.Format_ARGB32)
-        image.fill(QColor(Qt.red))
+        image = QImage(QSize(200, 200), QImage.Format.Format_ARGB32)
+        image.fill(QColor(Qt.GlobalColor.red))
         pixelFormat = image.pixelFormat()
         print(pixelFormat.greenSize())
         self.assertEqual(pixelFormat.alphaSize(), 8)
@@ -30,9 +31,10 @@ class QPixelFormatTest(UsesQApplication):
         self.assertEqual(pixelFormat.bitsPerPixel(), 32)
 
     def testHelpers(self):
-        format = qPixelFormatRgba(8, 8, 8, 8, QPixelFormat.UsesAlpha,
-                                  QPixelFormat.AtBeginning, QPixelFormat.Premultiplied,
-                                  QPixelFormat.UnsignedByte)
+        format = qPixelFormatRgba(8, 8, 8, 8, QPixelFormat.AlphaUsage.UsesAlpha,
+                                  QPixelFormat.AlphaPosition.AtBeginning,
+                                  QPixelFormat.AlphaPremultiplied.Premultiplied,
+                                  QPixelFormat.TypeInterpretation.UnsignedByte)
         self.assertEqual(format.redSize(), 8)
 
 
index dd658af523c0790110aa51d5354c50febed0e7d0..e78774eec66a01fe91d66405dbbcedd7325b1f3e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b80ae2ad55e613594eafffdfbf6a17ac6a97c171..0451b9839f20d3c9c3f801643efe66109fc18677 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -16,6 +17,11 @@ from PySide6.QtCore import QFile, QIODevice, QObject, QSize, Qt
 
 
 class QPixmapTest(UsesQApplication):
+
+    def setUp(self):
+        super().setUp()
+        self._sample_file = Path(__file__).resolve().parent / 'sample.png'
+
     def testQVariantConstructor(self):
         obj = QObject()
         pixmap = QPixmap()
@@ -27,18 +33,19 @@ class QPixmapTest(UsesQApplication):
         self.assertTrue(pixmap.size().height(), 20)
 
     def testQStringConstructor(self):
-        pixmap = QPixmap("Testing!")
+        pixmap = QPixmap("Testing!")  # noqa: F841
 
     def testQPixmapLoadFromDataWithQFile(self):
-        f = QFile(os.path.join(os.path.dirname(__file__), 'sample.png'))
-        self.assertTrue(f.open(QIODevice.ReadOnly))
+        f = QFile(self._sample_file)
+        self.assertTrue(f.open(QIODevice.OpenModeFlag.ReadOnly))
         data = f.read(f.size())
         f.close()
         pixmap = QPixmap()
         self.assertTrue(pixmap.loadFromData(data))
 
     def testQPixmapLoadFromDataWithPython(self):
-        data = open(os.path.join(os.path.dirname(__file__), 'sample.png'), 'rb').read()
+        with self._sample_file.open('rb') as f:
+            data = f.read()
         pixmap = QPixmap()
         self.assertTrue(pixmap.loadFromData(data))
 
@@ -47,8 +54,9 @@ class QPixmapToImage(UsesQApplication):
 
     def testFilledImage(self):
         '''QPixmap.fill + toImage + image.pixel'''
+        red = QColor(Qt.GlobalColor.red)
         pixmap = QPixmap(100, 200)
-        pixmap.fill(Qt.red)  # Default Qt.white
+        pixmap.fill(red)  # Default Qt.GlobalColor.white
 
         self.assertEqual(pixmap.height(), 200)
         self.assertEqual(pixmap.width(), 100)
@@ -59,9 +67,8 @@ class QPixmapToImage(UsesQApplication):
         self.assertEqual(image.width(), 100)
 
         pixel = image.pixel(10, 10)
-        self.assertEqual(pixel, QColor(Qt.red).rgba())
+        self.assertEqual(pixel, red.rgba())
 
 
 if __name__ == '__main__':
     unittest.main()
-
index 2ecb439d3d2bb165c7605468e3dcb663185ccf43..54ad9d7aee390bb8dcc7c5575e93eb3aa3715d05 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -30,8 +31,8 @@ class QPixmapCacheTest(UsesQApplication):
         pm3 = QPixmap()
         ok = QPixmapCache.find('img', pm3)
         self.assertTrue(ok)
-        b1 = QPixmapCache.find('img').toImage().bits()
-        b2 = pm3.toImage().bits()
+        b1 = QPixmapCache.find('img').toImage().bits()  # noqa: F841
+        b2 = pm3.toImage().bits()  # noqa: F841
         self.assertEqual(QPixmapCache.find('img').toImage().bits(), pm3.toImage().bits())
 
     def testWithKey(self):
@@ -53,4 +54,3 @@ class QPixmapCacheTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 8a283fddc286b8ecf4797c6ecda40dd89e987678..ea5a73db896c5cf5abd29b30b1d027c94efaff88 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index ebab42b151e24a85d43a341c991f5ee7181b6d82..5c48cf3eb9701b9a89610771d6dba7c3047ad254 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 4920902d5a24fbec91c52a688bb183b434a84cfc..09fcd46e8add1e3a0c628e97cf57929cb9eea5e6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit test for QBackingStore, QRasterWindow and QStaticText'''
 
@@ -27,7 +28,7 @@ class StaticTextRasterWindow(QRasterWindow):
     def paintEvent(self, event):
         clientRect = QRect(QPoint(0, 0), self.size())
         with QPainter(self) as painter:
-            painter.fillRect(clientRect, QColor(Qt.red))
+            painter.fillRect(clientRect, QColor(Qt.GlobalColor.red))
             painter.drawStaticText(QPoint(10, 10), self.text)
 
 
@@ -42,16 +43,16 @@ class TextDocumentWindow(QRasterWindow):
     def paintEvent(self, event):
         with QPainter(self) as painter:
             clientRect = QRect(QPoint(0, 0), self.size())
-            painter.fillRect(clientRect, QColor(Qt.white))
+            painter.fillRect(clientRect, QColor(Qt.GlobalColor.white))
             ctx = QAbstractTextDocumentLayout.PaintContext()
             ctx.clip = clientRect
 
             sel = QAbstractTextDocumentLayout.Selection()
             cursor = QTextCursor(self.m_document)
-            cursor.movePosition(QTextCursor.Start)
-            cursor.movePosition(QTextCursor.NextWord, QTextCursor.KeepAnchor)
+            cursor.movePosition(QTextCursor.MoveOperation.Start)
+            cursor.movePosition(QTextCursor.MoveOperation.NextWord, QTextCursor.MoveMode.KeepAnchor)
             sel.cursor = cursor
-            sel.format.setForeground(Qt.red)
+            sel.format.setForeground(Qt.GlobalColor.red)
             ctx.selections = [sel]
 
             self.m_document.documentLayout().draw(painter, ctx)
index df14fc3e052f51d5b75d5a39022c327f9b03ed24..97410f925ddbace90666b007511ad0d38079b57f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f2650589acff53094f20a39aad0baa4f897022c6..b093a01a8c201a88a8fb6e1a6cff80bf4af9b92d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test the QShortcut constructor'''
 
@@ -43,8 +44,8 @@ class QAppPresence(unittest.TestCase):
         self.qapp = QGuiApplication([])
         f = Foo()
 
-        self.sc = MyShortcut(QKeySequence(Qt.Key_Return), f, f.slot_of_foo)
-        self.scstd = MyShortcut(QKeySequence.Copy, f, f.slot_of_copy)
+        self.sc = MyShortcut(QKeySequence(Qt.Key.Key_Return), f, f.slot_of_foo)
+        self.scstd = MyShortcut(QKeySequence.StandardKey.Copy, f, f.slot_of_copy)
         QTimer.singleShot(0, self.init)
         self.qapp.exec()
         self.assertEqual(f.ok, True)
index 55aca91136793577ec296e5a572005bf0a43cc44..683b5825618a3d1801d252b90576a96561a33fd7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -66,7 +67,7 @@ class QStandardItemModelRef(UsesQApplication):
 
         for r in range(3):
             my_row = model.takeRow(0)
-            my_row = None
+            my_row = None  # noqa: F841
             for c in range(5):
                 # only rest 1 reference
                 self.assertEqual(sys.getrefcount(items[r][c]), 2)
@@ -82,4 +83,3 @@ class QStandardItemModelRef(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index f68374263f0e1a6188205f1ac7b4539fec1f7f9d..b91675eebd77c5d51cb58b0f2e0d45140328cba8 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests conversions of QString to and from QKeySequence.'''
 
@@ -39,4 +40,3 @@ class QStringQKeySequenceTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index d2b21d30de5b6e7d4c40fb5557964f5438f8557a..6d026fd22813df4905f92a7bc6d67e6b70bccf42 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit test for QStyleHints'''
 
@@ -12,13 +13,13 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from helper.usesqapplication import UsesQApplication
 from PySide6.QtGui import QStyleHints
+from helper.usesqapplication import UsesQApplication
 
 
 class QStyleHintsTest(UsesQApplication):
     def test(self):
-        styleHints = self.app.styleHints()
+        styleHints: QStyleHints = self.app.styleHints()
         self.assertTrue(styleHints.startDragDistance() > 0)
 
 
index 2ac72df569fa1641d1f6f1fbf83dc9a3e07c3170..7d4a8517717f546bb316dec18a7033266f1f7158 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,10 +8,10 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from PySide6.QtGui import QPageRanges, Qt
+from PySide6.QtGui import QPageRanges, Qt  # noqa: E402
 
 
 class QTextDocumentFunctions(unittest.TestCase):
@@ -18,7 +19,7 @@ class QTextDocumentFunctions(unittest.TestCase):
     def testFunctions(self):
         self.assertFalse(Qt.mightBeRichText('bla'))
         self.assertTrue(Qt.mightBeRichText('<html><head/><body><p>bla</p></body></html>'))
-        html = Qt.convertFromPlainText("A & B", Qt.WhiteSpaceNormal)
+        html = Qt.convertFromPlainText("A & B", Qt.WhiteSpaceMode.WhiteSpaceNormal)
         self.assertEqual(html, '<p>A &amp; B</p>')
 
 
index b74c6704f82d181a72744515b06f83a3fb7e78b5..39b2fef390b52b33a3ceca03e3c49ffeac4865f0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -39,4 +40,3 @@ class QTextDocumentTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 4f72b98e731a5feec8825b5ce01e21ae3096c804..5ca207794494b74b017e8295f1395d9a9343a9e4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -20,7 +21,7 @@ class QTextDocumentWriterTest(unittest.TestCase):
         text = 'foobar'
         doc = QTextDocument(text)
         b = QBuffer()
-        b.open(QBuffer.ReadWrite)
+        b.open(QBuffer.OpenModeFlag.ReadWrite)
         writer = QTextDocumentWriter(b, bytes("plaintext", "UTF-8"))
         writer.write(doc)
         b.close()
index c34a4c98af4bbc26143a771d55701d98c57988d5..f9ecafd4d8f720ed0905f70a77dc718865a9b1ff 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -30,10 +31,9 @@ class QTextLineTest(UsesQApplication):
 
     def testTextOption(self):
         """PYSIDE-2088, large enum values causing MSVC issues."""
-        v = QTextOption.IncludeTrailingSpaces | QTextOption.ShowTabsAndSpaces
+        v = QTextOption.Flag.IncludeTrailingSpaces | QTextOption.Flag.ShowTabsAndSpaces
         self.assertEqual(v.value, 2147483649)
 
 
 if __name__ == '__main__':
     unittest.main()
-
index dc51a74e6a8403693c88d62ede7bebf420fd8167..3fdd7f9cac24dd8032a7fa760afd6d0fd04838f3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -11,7 +12,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QPointF
-from PySide6.QtGui import QTransform, QPolygonF, QPolygonF, QQuaternion, QVector3D
+from PySide6.QtGui import QTransform, QPolygonF, QQuaternion
 
 
 class QTransformTest(unittest.TestCase):
@@ -87,4 +88,3 @@ class QTransformTest(unittest.TestCase):
 
 if __name__ == "__main__":
     unittest.main()
-
index 2858e71754ed9d7ac1b95a49241f9b7d56726d86..d50b12a8762c1406488c5827c09639dd8ef2e13d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,14 +11,13 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-import PySide6
-from PySide6.QtCore import QPoint
+# For self.original
+import PySide6  # noqa
 from PySide6.QtGui import QMatrix2x2, QMatrix2x3, QMatrix2x4
 from PySide6.QtGui import QMatrix3x2, QMatrix3x3, QMatrix3x4
 from PySide6.QtGui import QMatrix4x2, QMatrix4x3, QMatrix4x4
 from PySide6.QtGui import QVector2D, QVector3D, QVector4D
-from PySide6.QtGui import QColor, QTransform, QKeySequence, QQuaternion
-from PySide6.QtGui import QPolygon
+from PySide6.QtGui import QTransform, QQuaternion
 
 
 class ReprCopyHelper:
@@ -52,45 +52,50 @@ class QVector4DReprCopy(ReprCopyHelper, unittest.TestCase):
         self.original = QVector4D(1, 2, 3, 4)
 
 
-# Avoid these tests until get gcc fixed
-# Related bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43247
-"""
 class QMatrix2x2ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix2x2([1, 2, 3, 4])
 
+
 class QMatrix2x3ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix2x3([1, 2, 3, 4, 5, 6])
 
+
 class QMatrix2x4ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix2x4([1, 2, 3, 4, 5, 6, 7, 8])
 
+
 class QMatrix3x2ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix3x2([1, 2, 3, 4, 5, 6])
 
+
 class QMatrix3x3ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix3x3([1, 2, 3, 4, 5, 6, 7, 8, 9])
 
+
 class QMatrix3x4ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix3x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
 
+
 class QMatrix4x2ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix4x2([1, 2, 3, 4, 5, 6, 7, 8])
 
+
 class QMatrix4x3ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix4x3([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
 
+
 class QMatrix4x4ReprCopy(ReprCopyHelper, unittest.TestCase):
     def setUp(self):
         self.original = QMatrix4x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
-"""
+
 
 if __name__ == '__main__':
     unittest.main()
index 6e9a661cee0284337c91cdf391f6b6122641ab2c..232f90c922e92928768a03bcfea9165ac45ad558 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 3603d8190194d3e9f8ec95a4965e1a3b728e1a1b..d5502d1703258fe45237a57401ae03e4f25a4b59 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test data for QImage'''
 
index 4ded7763c8bfa926b5eb539ef0700a075cb483d7..9e5c879da287862dfdd16bccbea9ab41474de390 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b25e643f604955e5b14e3000fc0cfafeb94a0ab7..2cf8376bd6ae5ffbcdd485ad593ed760063274c7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtHelp import QHelpEngineCore, QHelpSearchEngine, QHelpSearchResult
+from PySide6.QtHelp import QHelpEngineCore, QHelpSearchEngine
 
 from helper.usesqapplication import UsesQApplication
 
index 8fbd55f02e2d619ac487dd09948b4dc80cf520a0..d2297af63dba9b449cda78740e77c17047a97c46 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit test for Location'''
 
index af359e5259b92b2ab56dc61e89e441d17d89cf79..fd70dd4ce1e04b81e474c2cb3eeb86ddf4a9a30b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QHttp'''
 
@@ -47,7 +48,15 @@ class testAudioDevices(UsesQApplication):
             return
         size = 256
         byte_array = QByteArray(size, '7')
-        buffer = QAudioBuffer(byte_array, self._devices[0].preferredFormat())
+        device = self._devices[0]
+        format = device.preferredFormat()
+        # Observed to be "Unknown" on Linux
+        if format.sampleFormat() == QAudioFormat.SampleFormat.Unknown:
+            sample_formats = device.supportedSampleFormats()
+            if sample_formats:
+                format.setSampleFormat(sample_formats[0])
+                format.setSampleRate(48000)
+        buffer = QAudioBuffer(byte_array, format)
         self.assertEqual(buffer.byteCount(), 256)
         data = buffer.data()
         actual_byte_array = QByteArray(bytearray(data))
index 968558a1376e43e1acfccb2e33b33ea6746ec1c9..a25ab3e3fecf25feed63719d53193f2f3feea094 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtMultimediaWidgets'''
 
index 87711b278155bd194f836c61a62d7251d0ef2471..3aab2fee5556ea01cb2abf37550f98d96dd4a546 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QHttp'''
 
index 515220c80ce0d5ed9ec2ab06e9a9bfb0de6d96a1..892bf03247ac2d61f35381866887cc42156b635b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' unit test for BUG #1084 '''
 
index f28ddb36955a8c8637621ef884d1cc305a4d9287..869b06e836087267c1642b3d1b6c938ba2c597e9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -42,7 +43,8 @@ class HttpSignalsCase(UsesQApplication):
         self.assertTrue(self.server.listen())
         self.client = QTcpSocket()
         self.client.connected.connect(self.onClientConnect)
-        self.client.connectToHost(QHostAddress(QHostAddress.LocalHost), self.server.serverPort())
+        self.client.connectToHost(QHostAddress(QHostAddress.SpecialAddress.LocalHost),
+                                  self.server.serverPort())
 
     def done(self):
         self.serverConnection.close()
index c50a6b5d4c2338da061af258f08ec67b180f244e..d138478a5c1cb7121f6668bb8568ce8270a7f170 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QDnsLookup'''
 
@@ -22,7 +23,7 @@ class DnsLookupTestCase(unittest.TestCase):
 
     def setUp(self):
         self._app = QCoreApplication([])
-        self._lookup = QDnsLookup(QDnsLookup.ANY, 'www.qt.io')
+        self._lookup = QDnsLookup(QDnsLookup.Type.ANY, 'www.qt.io')
         self._lookup.finished.connect(self._finished)
 
     def tearDown(self):
@@ -31,7 +32,7 @@ class DnsLookupTestCase(unittest.TestCase):
         gc.collect()
 
     def _finished(self):
-        if self._lookup.error() == QDnsLookup.NoError:
+        if self._lookup.error() == QDnsLookup.Error.NoError:
             nameRecords = self._lookup.canonicalNameRecords()
             if nameRecords:
                 print(nameRecords[0].name())
index 8d8d2cae448bcc9c747093addf771f10aa72904c..678646f378fb86882b6d4d54e0993e88af376f04 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for QHostInfo.'''
 
@@ -35,7 +36,7 @@ class Receiver(QObject):
     @Slot(QHostInfo)
     def info_received(self, host_info):
         name = host_info.hostName()
-        if host_info.error() == QHostInfo.NoError:
+        if host_info.error() == QHostInfo.HostInfoError.NoError:
             addresses = [a.toString() for a in host_info.addresses()]
             addresses_str = ', '.join(addresses)
             print(f'"{name}" resolved to {addresses_str}')
index 67b103d3c1be10af5dde6ff71f24c980dee44e89..6cd3f1993cd228d1a7d808ac736636fb339625a4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QIPv6Address'''
 
index db7a90da9e66525794b52ae7ab2300877bf71012..b0eecdbc66029a341cb3435d76c9efd59b2922ee 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QPasswordDigestor'''
 
@@ -13,13 +14,13 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QByteArray, QCryptographicHash
+from PySide6.QtCore import QCryptographicHash
 from PySide6.QtNetwork import QPasswordDigestor
 
 
 class TestPasswordDigestor(unittest.TestCase):
     def test(self):
-        b = QPasswordDigestor.deriveKeyPbkdf1(QCryptographicHash.Sha1,
+        b = QPasswordDigestor.deriveKeyPbkdf1(QCryptographicHash.Algorithm.Sha1,
                                               b'test', b'saltnpep', 10, 20)
         self.assertEqual(b.size(), 20)
 
index 845afdfae4959a86ab422a213798ddd85d7c1a83..7d1b127d2df2576ac996cd46822db0e30cb7167a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QTCPServer'''
 
index 9508498013de303f9ec12f60029d2bd541e60f4a..4af8fccde60d5e141cf331342a360a06a6607f14 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QUdpSocket'''
 
@@ -13,7 +14,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QUrl, QObject, SIGNAL, QCoreApplication, QTimer
+from PySide6.QtCore import QCoreApplication
 from PySide6.QtNetwork import QUdpSocket, QHostAddress
 
 
@@ -31,7 +32,7 @@ class HttpSignalsCase(unittest.TestCase):
         self.socket = QUdpSocket()
 
         self.server = QUdpSocket()
-        self.server.bind(QHostAddress(QHostAddress.LocalHost), 45454)
+        self.server.bind(QHostAddress(QHostAddress.SpecialAddress.LocalHost), 45454)
 
     def tearDown(self):
         # Release resources
@@ -42,7 +43,7 @@ class HttpSignalsCase(unittest.TestCase):
         gc.collect()
 
     def sendPackage(self):
-        addr = QHostAddress(QHostAddress.LocalHost)
+        addr = QHostAddress(QHostAddress.SpecialAddress.LocalHost)
         self.socket.writeDatagram(bytes('datagram', "UTF-8"), addr, 45454)
 
     def callback(self):
index a73c52153d6947916b6765b600d8dabd29cb2795..2a2053a97ce889c482ffdeacae9f6ed59dff2020 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtNfc'''
 
index e4ff6feb4c217af61721219602ff573cab3dcc7f..5146cc196b302cf0b180cad29edaed28a2f6c59c 100644 (file)
@@ -1,9 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit tests for QOpenGLBuffer'''
 
-import ctypes
 import os
 import sys
 import unittest
@@ -14,32 +14,32 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
-from PySide6.QtGui import QOffscreenSurface, QOpenGLContext, QSurface, QWindow
+from PySide6.QtGui import QOffscreenSurface, QOpenGLContext, QSurface, QWindow, QSurfaceFormat
 from PySide6.QtOpenGL import QOpenGLBuffer
 
 
 def createSurface(surfaceClass):
-    if surfaceClass == QSurface.Window:
+    if surfaceClass == QSurface.SurfaceClass.Window:
         window = QWindow()
-        window.setSurfaceType(QWindow.OpenGLSurface)
+        window.setSurfaceType(QWindow.SurfaceType.OpenGLSurface)
         window.setGeometry(0, 0, 10, 10)
         window.create()
         return window
-    elif surfaceClass == QSurface.Offscreen:
+    elif surfaceClass == QSurface.SurfaceClass.Offscreen:
         # Create a window and get the format from that.  For example, if an EGL
         # implementation provides 565 and 888 configs for PBUFFER_BIT but only
         # 888 for WINDOW_BIT, we may end up with a pbuffer surface that is
         # incompatible with the context since it could choose the 565 while the
         # window and the context uses a config with 888.
-        format = QSurfaceFormat
-        if format.redBufferSize() == -1:
+        _format = QSurfaceFormat
+        if _format.redBufferSize() == -1:
             window = QWindow()
-            window.setSurfaceType(QWindow.OpenGLSurface)
+            window.setSurfaceType(QWindow.SurfaceType.OpenGLSurface)
             window.setGeometry(0, 0, 10, 10)
             window.create()
-            format = window.format()
+            _format = window.format()
         offscreenSurface = QOffscreenSurface()
-        offscreenSurface.setFormat(format)
+        offscreenSurface.setFormat(_format)
         offscreenSurface.create()
         return offscreenSurface
     return 0
@@ -47,7 +47,7 @@ def createSurface(surfaceClass):
 
 class QOpenGLBufferTest(UsesQApplication):
     def testBufferCreate(self):
-        surface = createSurface(QSurface.Window)
+        surface = createSurface(QSurface.SurfaceClass.Window)
         ctx = QOpenGLContext()
         ctx.create()
         ctx.makeCurrent(surface)
@@ -59,7 +59,7 @@ class QOpenGLBufferTest(UsesQApplication):
         self.assertTrue(buf.create())
         self.assertTrue(buf.isCreated())
 
-        self.assertEqual(buf.type(), QOpenGLBuffer.VertexBuffer)
+        self.assertEqual(buf.type(), QOpenGLBuffer.Type.VertexBuffer)
 
         buf.bind()
         buf.allocate(128)
index 3e6bc4c9d99ee25ab427b870b09cea43b61a6d1a..5455557bb9d6ffb75f49e6a6fd872ba37daf5985 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit test for QOpenGLContext, QOpenGLTexture, QOpenGLWindow and related classes'''
 
@@ -15,8 +16,7 @@ init_test_paths(False)
 from helper.usesqapplication import UsesQApplication
 
 from PySide6.QtCore import QSize, QTimer, Qt
-from PySide6.QtGui import (QColor, QGuiApplication, QImage, QOpenGLContext,
-                           QSurfaceFormat)
+from PySide6.QtGui import QColor, QImage, QSurfaceFormat
 from PySide6.QtOpenGL import QOpenGLTexture, QOpenGLWindow
 
 
@@ -44,13 +44,13 @@ class OpenGLWindow(QOpenGLWindow):
     def initializeGL(self):
         profile = QOpenGLVersionProfile()
         profile.setVersion(1, 3)
-        profile.setProfile(QSurfaceFormat.CompatibilityProfile)
+        profile.setProfile(QSurfaceFormat.OpenGLContextProfile.CompatibilityProfile)
         self.m_functions = QOpenGLVersionFunctionsFactory.get(profile)
         self.m_functions.initializeOpenGLFunctions()
 
         print("GL_MAX_LIGHTS=", self.m_functions.glGetIntegerv(GL.GL_MAX_LIGHTS))
-        image = QImage(QSize(200, 200), QImage.Format_RGBA8888)
-        image.fill(QColor(Qt.red))
+        image = QImage(QSize(200, 200), QImage.Format.Format_RGBA8888)
+        image.fill(QColor(Qt.GlobalColor.red))
         self.m_texture = QOpenGLTexture(image)
 
     def paintGL(self):
@@ -83,7 +83,8 @@ class OpenGLWindow(QOpenGLWindow):
 
 
 class QOpenGLWindowTest(UsesQApplication):
-    # On macOS, glClear(), glViewport() are rejected due to GLbitfield/GLint not being resolved properly
+    # On macOS, glClear(), glViewport() are rejected due to
+    # GLbitfield/GLint not being resolved properly
     def test(self):
         openGlWindow = OpenGLWindow()
         openGlWindow.resize(640, 480)
index d8e6702478f048b3b9f83edaf137db9f731c54b4..4538895eba65ffcccc86a40ed26a0e42f142fa0c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit test for Positioning'''
 
index 1ba5b35e2234ea5279ab0dc5e309a60933aa6620..aaeb249fa9e3a46c46e87c9cbcb411c70d471a95 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 97b3505da37b4da7433f1679298f1116762d5913..8fe924edbb4c3f347558fd3d216604f4431ba5bc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -11,8 +12,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtGui import QTextCursor
-from PySide6.QtPrintSupport import QPrinter, QPrinterInfo
-from PySide6.QtWidgets import QLayout, QWidget, QGraphicsLayout, QGraphicsLayoutItem
+from PySide6.QtWidgets import QLayout, QGraphicsLayout, QGraphicsLayoutItem
 
 from helper.usesqapplication import UsesQApplication
 
@@ -62,4 +62,3 @@ class ReturnsQuadruplesOfNumbers(UsesQApplication):
 
 if __name__ == "__main__":
     unittest.main()
-
index 69ca5c18dbc8011ad187d124b2c85934d3617d9d..58fbfdebc99976b4b2607712939873ad654502c8 100644 (file)
@@ -1,10 +1,10 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
 import sys
-import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
@@ -26,7 +26,7 @@ def register_qml_types():
 
 
 def main():
-    app = QGuiApplication([])
+    app = QGuiApplication([])  # noqa
 
     # reg qml types here
     register_qml_types()
index d81a99d9400bc565620151cbd9d4afb9c85280ab..81537b4a5732158d0a10854cca85f0240a41a73d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''
 Test bug 451: http://bugs.openbossa.org/show_bug.cgi?id=451
index 7743ee3fd39d16613ab4b51902620d25263888a3..02ab16ebfc6b0834f00552902e9485b939585f79 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index eb43973f64f2259986fe9874bca2fb6fe0ac1cc4..79068d0ab5882106c270137ba18ac9bf70227dc6 100644 (file)
@@ -1,17 +1,15 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
-import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from helper.helper import adjust_filename
-
 from PySide6.QtCore import QUrl
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlEngine, QQmlComponent
@@ -25,4 +23,3 @@ component = QQmlComponent(engine)
 file = Path(__file__).resolve().parent / 'foo.qml'
 assert (not file.is_file())
 component.loadUrl(QUrl.fromLocalFile(file))
-
index 56c1e70f15ae0a264379fb970c70075d153271b2..b0a2fb4ed066636c31b685331ddd4ee6df84c5bd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 0e7858b6c4b09b3d075f3b64cd201e4047239404..e7599020891a690bfc12f5870450aa1ed42b823f 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # Copyright (C) 2011 Thomas Perl <m@thp.io>
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # Test case for PySide bug 814
 # http://bugs.pyside.org/show_bug.cgi?id=814
@@ -36,13 +37,13 @@ class ListModel(QAbstractListModel):
         super().__init__()
 
     def roleNames(self):
-        return {Qt.DisplayRole: b'pysideModelData'}
+        return {Qt.ItemDataRole.DisplayRole: b'pysideModelData'}
 
     def rowCount(self, parent=QModelIndex()):
         return 3
 
     def data(self, index, role):
-        if index.isValid() and role == Qt.DisplayRole:
+        if index.isValid() and role == Qt.ItemDataRole.DisplayRole:
             return 'blubb'
         return None
 
@@ -62,4 +63,3 @@ class TestBug814(TimedQGuiApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 4331e424fd7cedcfa0133e3075369973f0779be3..bf2a88cc11f9f213c5cec03978d966cef02fd1a6 100644 (file)
@@ -10,4 +10,3 @@ ListView {
     delegate: Text { text: pysideModelData }
     model: pythonModel
 }
-
index a8bd304ecb3b6910387bc90f278b5544d79ef9a8..47d734931433bf064ac35ac23812ed1dddd83370 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 This is the corrected version for Python 3.
@@ -19,9 +20,8 @@ from helper.helper import quickview_errorstring
 
 from PySide6.QtCore import Qt, QUrl, QTimer
 from PySide6.QtGui import QGuiApplication, QPen
-from PySide6.QtWidgets import QGraphicsItem
 from PySide6.QtQml import qmlRegisterType
-from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem
+from PySide6.QtQuick import QQuickView, QQuickPaintedItem
 
 paintCalled = False
 
@@ -52,7 +52,7 @@ class Bug825 (C):
 
     def paint(self, painter):
         global paintCalled
-        pen = QPen(Qt.black, 2)
+        pen = QPen(Qt.GlobalColor.black, 2)
         painter.setPen(pen)
         painter.drawPie(self.boundingRect(), 0, 128)
         paintCalled = True
index c44fa75f4bb1aa3411ef6b71e67b426cf105d361..88d288ee6eeacc02f85fbbec8492554890673f51 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 This is the now incorrect old version from Python 2.
@@ -19,9 +20,8 @@ from helper.helper import quickview_errorstring
 
 from PySide6.QtCore import Qt, QUrl, QTimer
 from PySide6.QtGui import QGuiApplication, QPen
-from PySide6.QtWidgets import QGraphicsItem
 from PySide6.QtQml import qmlRegisterType
-from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem
+from PySide6.QtQuick import QQuickView, QQuickPaintedItem
 
 paintCalled = False
 
@@ -30,7 +30,7 @@ class MetaA(type):
     pass
 
 
-class A(object):
+class A:
     __metaclass__ = MetaA
 
 
@@ -52,7 +52,7 @@ class Bug825 (C):
 
     def paint(self, painter):
         global paintCalled
-        pen = QPen(Qt.black, 2)
+        pen = QPen(Qt.GlobalColor.black, 2)
         painter.setPen(pen)
         painter.drawPie(self.boundingRect(), 0, 128)
         paintCalled = True
index 947eb494e0894482356ecffcb285625773a18e7b..557cf967b8d60a351e33620070ff2b25cac42a05 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # Copyright (C) 2011 Thomas Perl <m@thp.io>
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # Testcase for PySide bug 847
 # Released under the same terms as PySide itself
@@ -49,9 +50,9 @@ class TestQML(UsesQApplication):
         file = Path(__file__).resolve().parent / 'bug_847.qml'
         self.assertTrue(file.is_file())
         view.setSource(QUrl.fromLocalFile(file))
-        while view.status() == QQuickView.Loading:
+        while view.status() == QQuickView.Status.Loading:
             self.app.processEvents()
-        self.assertEqual(view.status(), QQuickView.Ready)
+        self.assertEqual(view.status(), QQuickView.Status.Ready)
         self.assertTrue(view.rootObject(), quickview_errorstring(view))
         view.rootObject().setProperty('pythonObject', view)
 
@@ -67,4 +68,3 @@ class TestQML(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 18efd80ce4ac7618a8ed4fdec50129ebb0190116..221ae3df6310750d3008aab907471c4fa793d44d 100644 (file)
@@ -34,4 +34,3 @@ Rectangle {
         }
     }
 }
-
index 3095eba2af536cf658492be0bb300b880051f2fe..94ce2cc3c4a13bfb2b2487bf9212147077d69452 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -36,5 +37,3 @@ class TestBug915(TimedQGuiApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
-
index 085e9a68f0e1bf607af3f0f59420adac1c8e85f6..1c0281101a5fd9b37bfe017f26f99ef235ab98c9 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
-import sys
 import os
 import sys
 import unittest
index 8a512d06fba88c696502bdb0e1fa8fb694707841..5cb4bb5389a1fe68ba9a2727ce88fdc193c29475 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 868c584e2179df8db51dddef250763633f0ea562..c37d8dda8ef9ba8dee078b6004bcf75bc494fd16 100644 (file)
@@ -1,18 +1,15 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
-import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from helper.helper import adjust_filename
-from helper.usesqapplication import UsesQApplication
-
 from PySide6.QtCore import QUrl
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQuick import QQuickView
@@ -28,4 +25,3 @@ item = contentItem.childAt(100, 100)
 
 # it CAN NOT crash here
 print(item)
-
index 501c221c3f80b24a725f1c2558a6cc96994d2b13..85c8c294753fe7231a6d8270278f1fcf60a0afbd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -12,7 +13,7 @@ init_test_paths(False)
 
 from helper.helper import quickview_errorstring
 from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import QCoreApplication, QTimer, QUrl, Slot
+from PySide6.QtCore import QUrl, Slot
 from PySide6.QtQml import QQmlPropertyMap
 from PySide6.QtQuick import QQuickView
 
index 2e60aec4feb4ca5d8088d9b43f43eea043ee45fa..97ceebf65bf3b0a44a6cad2df365085900e86b76 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''
 Test case for bug #442
@@ -14,14 +15,13 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import quickview_errorstring
-from helper.timedqguiapplication import TimedQGuiApplication
-from PySide6.QtCore import QObject, QUrl, SIGNAL
-from PySide6.QtGui import QColor
-from PySide6.QtQuick import QQuickItem, QQuickView
+from helper.helper import quickview_errorstring  # noqa: E402
+from helper.timedqguiapplication import TimedQGuiApplication  # noqa: E402
+from PySide6.QtCore import QObject, QUrl, SIGNAL  # noqa: E402
+from PySide6.QtQuick import QQuickView  # noqa: E402
 
 
 class TestConnectionWithInvalidSignature(TimedQGuiApplication):
index 4554d4b31ac69aece6ed2ee084542eb7b2bcc399..d3ddb625cb976fad13713c7d15c4ca66ebc20ffa 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """Test grouped properties (PYSIDE-1836)."""
 
index e2b530aaf35e010d40641310f9cafb9fd9a487a0..84627a9849ec3bcda8e0746a8b5a9a3ef3ad86c4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,15 +8,15 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import quickview_errorstring
-from helper.usesqapplication import UsesQApplication
+from helper.helper import quickview_errorstring  # noqa: E402
+from helper.usesqapplication import UsesQApplication  # noqa: E402
 
-from PySide6.QtCore import Slot, Property, Signal, QObject, QUrl
-from PySide6.QtQml import QJSEngine, qmlRegisterType
-from PySide6.QtQuick import QQuickView
+from PySide6.QtCore import Slot, Property, QObject, QUrl  # noqa: E402
+from PySide6.QtQml import QJSEngine, qmlRegisterType  # noqa: E402
+from PySide6.QtQuick import QQuickView  # noqa: E402
 
 test_error_message = "This is an error."
 
index 884600d29ad350527e10e5bff42f6c1671189c35..0ffa8fd21ec162cef4acdc542a907bd105c3e609 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 0042d6fd3fc3217ede1b892d7f09abb823717bb1..587a3b9b0dcf432b4d7fa24a7004c1227e7d8564 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index ea54e9e256510717d1872210ad8b9295718b0317..13c1857260787b4f2e11b121053c7462db1e5b8c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for QQmlApplicationEngine'''
 
@@ -14,7 +15,7 @@ init_test_paths(False)
 
 from helper.timedqguiapplication import TimedQGuiApplication
 
-from PySide6.QtCore import QUrl, QObject, QTimer, Qt
+from PySide6.QtCore import QTimer
 from PySide6.QtQml import QQmlApplicationEngine
 
 
index 5521c64fa5e06592ce356c79c4fa0e8a081c8298..c507603a6aa00e8e9260150c74f199def051d5f6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 12a73e39850cddf20007062269f3db5e5bd5df6d..f5f7d70ef23a900c11f79d8dc21ab5c1039ea663 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -9,15 +10,15 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import quickview_errorstring
+from helper.helper import quickview_errorstring  # noqa: E402
 
-from PySide6.QtCore import QObject, QUrl, Slot, QTimer
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQml import QQmlIncubationController, VolatileBool
-from PySide6.QtQuick import QQuickView
+from PySide6.QtCore import QObject, QUrl, Slot, QTimer  # noqa: E402
+from PySide6.QtGui import QGuiApplication  # noqa: E402
+from PySide6.QtQml import QQmlIncubationController, VolatileBool  # noqa: E402
+from PySide6.QtQuick import QQuickView  # noqa: E402
 
 
 class CustomIncubationController(QObject, QQmlIncubationController):
@@ -51,7 +52,7 @@ class TestBug(unittest.TestCase):
         view = QQuickView()
         controller = CustomIncubationController(self)
         view.engine().setIncubationController(controller)
-        view.setResizeMode(QQuickView.SizeRootObjectToView)
+        view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
         file = Path(__file__).resolve().parent / 'qqmlincubator_incubateWhile.qml'
         self.assertTrue(file.is_file())
         view.setSource(QUrl.fromLocalFile(file))
@@ -61,7 +62,7 @@ class TestBug(unittest.TestCase):
         root = view.rootObject()
         # The QML code will issue an interrupt signal after half of its items are loaded.
         root.shouldInterrupt.connect(controller.interrupter)
-        res = app.exec()
+        res = app.exec()  # noqa: F841
 
         itemsToCreate = root.property("itemsToCreate")
         loadedItems = root.property("loadedItems")
index abdb4529e76990d38a78c76428cc2c35a1316884..6700f2c3878c8e22e15ce2ef77bd48ac0faa1397 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QQmlNetwork'''
 
@@ -13,7 +14,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QUrl, QTimer
-from PySide6.QtGui import QGuiApplication, QWindow
+from PySide6.QtGui import QGuiApplication
 from PySide6.QtQuick import QQuickView
 from PySide6.QtQml import QQmlNetworkAccessManagerFactory
 from PySide6.QtNetwork import QNetworkAccessManager
@@ -64,7 +65,7 @@ class TestQQmlNetworkFactory(TimedQGuiApplication):
         self.assertTrue(view.rootObject(), quickview_errorstring(view))
         view.show()
 
-        self.assertEqual(view.status(), QQuickView.Ready)
+        self.assertEqual(view.status(), QQuickView.Status.Ready)
 
         timer = QTimer()
         timer.timeout.connect(check_done)
index 25341b0b203416fa402dd0020868fcfb50ff5ee0..f53b4f5a35d9bf55b6d7f28aaf084ebe84bc032f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 22650966938fb8bbbd04bf3bb2306e5d2dbf810a..eaca08e461712a81cceb2c5859b03746953bbed3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QQuickView'''
 
@@ -53,7 +54,7 @@ class TestQQuickView(TimedQGuiApplication):
         self.assertTrue(view.rootObject(), quickview_errorstring(view))
         view.show()
 
-        self.assertEqual(view.status(), QQuickView.Ready)
+        self.assertEqual(view.status(), QQuickView.Status.Ready)
         rootObject = view.rootObject()
         self.assertTrue(rootObject)
         context = QQmlEngine.contextForObject(rootObject)
@@ -80,7 +81,7 @@ class TestQQuickView(TimedQGuiApplication):
         self.assertTrue(view.rootObject(), quickview_errorstring(view))
         view.show()
 
-        self.assertEqual(view.status(), QQuickView.Ready)
+        self.assertEqual(view.status(), QQuickView.Status.Ready)
 
 
 if __name__ == '__main__':
index dd300dc89a8592e35f2b2940067800851b9bb195..f6784f516cf4cd4ad05b700f335dd015a14b6bb8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b87b5aaaf8454aa51c602f4233344451a39733f7..ee8cb45f2378f40cfddcf70a3abb622ce97833c9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index d9a982d953e55863cfdb121c1193f3e2007a9d88..373e3004bf5fe075a454be4f11aed18a07c03dec 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,16 +8,15 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import qmlcomponent_errorstring
-from helper.timedqguiapplication import TimedQGuiApplication
+from helper.helper import qmlcomponent_errorstring  # noqa: E402
+from helper.timedqguiapplication import TimedQGuiApplication  # noqa: E402
 
-from PySide6.QtCore import Property, QObject, QUrl, qVersion
-from PySide6.QtGui import QGuiApplication, QRasterWindow
-from PySide6.QtQml import (QmlNamedElement, QmlForeign, QQmlEngine,
-                           QQmlComponent)
+from PySide6.QtCore import QObject, QUrl, qVersion  # noqa: E402
+from PySide6.QtGui import QRasterWindow  # noqa: 402
+from PySide6.QtQml import QmlNamedElement, QmlForeign, QQmlEngine, QQmlComponent  # noqa: E402
 
 
 """Test the QmlForeign decorator, letting the QQmlEngine create a QRasterWindow."""
index bbcc14635b661faa7e3c348ef0d728b9d4b2b4da..9c2b93ca9ddd31c8b6e0525a6ac727a5690d5103 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 335652e5de723c939a7514dc5e8b233cfc7e8f30..39e5ab75a62b22f96c6f23eeb024100a6f7e8962 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 6beca11317f42ee065d188371e4d6fc3faac8961..e0f24b3e2464c03865b62c9cf75c1ca051de0622 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,16 +8,16 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import quickview_errorstring
+from helper.helper import quickview_errorstring  # noqa: E402
 
-from PySide6.QtCore import Property, Signal, QTimer, QUrl, QObject, Slot
-from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import Property, QTimer, QUrl, QObject, Slot  # noqa: E402
+from PySide6.QtGui import QGuiApplication  # noqa: E402
 from PySide6.QtQml import (qmlRegisterSingletonType, qmlRegisterSingletonInstance,
-                           QmlElement, QmlSingleton, QJSValue)
-from PySide6.QtQuick import QQuickView
+                           QmlElement, QmlSingleton, QJSValue)  # noqa: E402
+from PySide6.QtQuick import QQuickView  # noqa: E402
 
 
 URI = "Singletons"
@@ -117,7 +118,7 @@ class TestQmlSupport(unittest.TestCase):
     def testIt(self):
         app = QGuiApplication([])
 
-        qObjectQmlTypeId = qmlRegisterSingletonType(SingletonQObject, URI, 1, 0,
+        qObjectQmlTypeId = qmlRegisterSingletonType(SingletonQObject, URI, 1, 0,  # noqa: F841
                                                     'SingletonQObjectNoCallback')
         qmlRegisterSingletonType(SingletonQObject, URI, 1, 0, 'SingletonQObjectCallback',
                                  singletonQObjectCallback)
@@ -125,9 +126,9 @@ class TestQmlSupport(unittest.TestCase):
         qmlRegisterSingletonType(URI, 1, 0, 'SingletonQJSValue', singletonQJSValueCallback)
 
         # Accepts only QObject derived types
-        l = [1, 2]
+        _list = [1, 2]
         with self.assertRaises(TypeError):
-            qmlRegisterSingletonInstance(SingletonQObject, URI, 1, 0, 'SingletonInstance', l)
+            qmlRegisterSingletonInstance(SingletonQObject, URI, 1, 0, 'SingletonInstance', _list)
 
         # Modify value on the instance
         s = SingletonQObject()
@@ -149,4 +150,5 @@ class TestQmlSupport(unittest.TestCase):
         self.assertTrue(view._singleton_instance_jsvalue_int)
 
 
-if __name__ == '__main__':    unittest.main()
+if __name__ == '__main__':
+    unittest.main()
index 6c9874f320cc7cfa61996fcc3138db40157ed7dc..b90c050116e64a9678b06865f6bcfcc4ef789a0e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,16 +8,16 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import quickview_errorstring
+from helper.helper import quickview_errorstring  # noqa: E402
 
-from PySide6.QtCore import Property, QObject, QTimer, QUrl
-from PySide6.QtGui import QGuiApplication, QPen, QColor, QPainter
-from PySide6.QtQml import (qjsEngine, qmlContext, qmlEngine, qmlRegisterType,
+from PySide6.QtCore import Property, QTimer, QUrl  # noqa: E402
+from PySide6.QtGui import QGuiApplication, QPen, QColor, QPainter  # noqa: E402
+from PySide6.QtQml import (qjsEngine, qmlContext, qmlEngine,  # noqa: E402
                            ListProperty, QmlElement, QmlNamedElement)
-from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem
+from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem  # noqa: E402
 
 
 QML_IMPORT_NAME = "Charts"
@@ -57,7 +58,7 @@ class PieSlice (QQuickPaintedItem):
         global paintCalled
         pen = QPen(self._color, 2)
         painter.setPen(pen)
-        painter.setRenderHints(QPainter.Antialiasing, True)
+        painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
         painter.drawPie(self.boundingRect(), self._fromAngle * 16, self._angleSpan * 16)
         paintCalled = True
 
index 3a4df69f6611ef20312a39097e7469fc14b615a7..944c011a467b02f8c39918b84b4fea82e8e917a2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,14 +8,12 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import qmlcomponent_errorstring
-
-from PySide6.QtCore import Property, QObject, QUrl
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQml import QmlElement, QmlUncreatable, QQmlEngine, QQmlComponent
+from PySide6.QtCore import Property, QObject, QUrl  # noqa: E402
+from PySide6.QtGui import QGuiApplication  # noqa: E402
+from PySide6.QtQml import QmlElement, QmlUncreatable, QQmlEngine, QQmlComponent  # noqa: E402
 
 noCreationReason = 'Cannot create an item of type: Uncreatable (expected)'
 
@@ -41,7 +40,7 @@ class Uncreatable(QObject):
 class TestQmlSupport(unittest.TestCase):
 
     def testIt(self):
-        app = QGuiApplication([])
+        app = QGuiApplication([])  # noqa: F841
 
         engine = QQmlEngine()
         file = Path(__file__).resolve().parent / 'registeruncreatable.qml'
@@ -49,7 +48,7 @@ class TestQmlSupport(unittest.TestCase):
         component = QQmlComponent(engine, QUrl.fromLocalFile(file))
 
         # Check that the uncreatable item produces the correct error
-        self.assertEqual(component.status(), QQmlComponent.Error)
+        self.assertEqual(component.status(), QQmlComponent.Status.Error)
         errorFound = False
         for e in component.errors():
             if noCreationReason in e.toString():
index f5b0f8bd3d4d6cf5b0aedaaac53ec0467d10839b..8fd939215cb88d8550241c91a7cc00c13f8e02ab 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,15 +8,15 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from helper.helper import quickview_errorstring
-from helper.timedqguiapplication import TimedQGuiApplication
+from helper.helper import quickview_errorstring  # noqa: E402
+from helper.timedqguiapplication import TimedQGuiApplication  # noqa: E402
 
-from PySide6.QtQuick import QQuickView
-from PySide6.QtCore import QObject, Signal, Slot, QUrl, QTimer, Property
-from PySide6.QtQml import QmlElement
+from PySide6.QtQuick import QQuickView  # noqa: E402
+from PySide6.QtCore import QObject, Signal, Slot, QUrl  # noqa: E402
+from PySide6.QtQml import QmlElement  # noqa: E402
 
 QML_IMPORT_NAME = "test.Obj"
 QML_IMPORT_MAJOR_VERSION = 1
index 240c0fd6edb4c35895982cdd0d01b6cee9efef8c..4ecc19da17fb859651897d666df81312a719ea91 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import json
 import os
index 33db6072e0047c99f7eebce635fd2af785521d0a..06cb3cdb133fb3f638a275c70a42efd2db4ba3d4 100644 (file)
@@ -13,4 +13,3 @@ ListView {
         Text { text: model.modelData.title }
     }
 }
-
index 33b2db08f26f3da97c707c4a14bcc4f059887cbd..2e45960cd715eacdda885d9c466a38116d3dc7c2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 212d6f7c1866a8b455a84bab4acc8a8742f70510..c03fe1e0bfc8ff95deb084fe6d365ef696ad0a53 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -11,7 +12,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from helper.timedqapplication import TimedQApplication
-from PySide6.QtCore import QCoreApplication, QObject, Slot, SIGNAL, SLOT
+from PySide6.QtCore import QCoreApplication, QObject, Slot, SLOT
 from PySide6.QtScxml import QScxmlStateMachine, QScxmlEvent
 
 
index 84bd1a92fbcb9f8792cdc6fd9fd2897be9b234db..cffc54bf28c51ba667941c66d0d2ee11e949e163 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QSensor'''
 
@@ -10,10 +11,10 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from PySide6.QtSensors import QSensor, QSensorReading
+from PySide6.QtSensors import QSensor  # noqa: E402
 
 
 class QSensorTest(unittest.TestCase):
index f1e54f2ac0c232cd0d09076316b97e705453aa35..ea55eb113625fdf760c7d2aed8240928a30097a8 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QSerialPort'''
 
@@ -10,36 +11,36 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo
-from PySide6.QtCore import QIODevice
+from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo  # noqa: E402
+from PySide6.QtCore import QIODevice  # noqa: E402
 
 
 class QSerialPortTest(unittest.TestCase):
     def testDefaultConstructedPort(self):
         serialPort = QSerialPort()
 
-        self.assertEqual(serialPort.error(), QSerialPort.NoError)
+        self.assertEqual(serialPort.error(), QSerialPort.SerialPortError.NoError)
         self.assertTrue(not serialPort.errorString() == "")
 
         # properties
-        defaultBaudRate = QSerialPort.Baud9600
+        defaultBaudRate = QSerialPort.BaudRate.Baud9600
         self.assertEqual(serialPort.baudRate(), defaultBaudRate)
-        self.assertEqual(serialPort.baudRate(QSerialPort.Input), defaultBaudRate)
-        self.assertEqual(serialPort.baudRate(QSerialPort.Output), defaultBaudRate)
-        self.assertEqual(serialPort.dataBits(), QSerialPort.Data8)
-        self.assertEqual(serialPort.parity(), QSerialPort.NoParity)
-        self.assertEqual(serialPort.stopBits(), QSerialPort.OneStop)
-        self.assertEqual(serialPort.flowControl(), QSerialPort.NoFlowControl)
+        self.assertEqual(serialPort.baudRate(QSerialPort.Direction.Input), defaultBaudRate)
+        self.assertEqual(serialPort.baudRate(QSerialPort.Direction.Output), defaultBaudRate)
+        self.assertEqual(serialPort.dataBits(), QSerialPort.DataBits.Data8)
+        self.assertEqual(serialPort.parity(), QSerialPort.Parity.NoParity)
+        self.assertEqual(serialPort.stopBits(), QSerialPort.StopBits.OneStop)
+        self.assertEqual(serialPort.flowControl(), QSerialPort.FlowControl.NoFlowControl)
 
-        self.assertEqual(serialPort.pinoutSignals(), QSerialPort.NoSignal)
+        self.assertEqual(serialPort.pinoutSignals(), QSerialPort.PinoutSignal.NoSignal)
         self.assertEqual(serialPort.isRequestToSend(), False)
         self.assertEqual(serialPort.isDataTerminalReady(), False)
 
         # QIODevice
-        self.assertEqual(serialPort.openMode(), QIODevice.NotOpen)
+        self.assertEqual(serialPort.openMode(), QIODevice.OpenModeFlag.NotOpen)
         self.assertTrue(not serialPort.isOpen())
         self.assertTrue(not serialPort.isReadable())
         self.assertTrue(not serialPort.isWritable())
index b47aecb77e310020027c45392dbae0872bac7b8e..40f74fac90bf3908cdaabdeeb625b873fbd19027 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index a3e70a3a74847fb67445cdc927932a4500f18773..731ea3dce718377452f088f1df4359d8896a6a49 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QtSql database creation, destruction and queries'''
 
@@ -15,7 +16,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
-from PySide6.QtWidgets import QApplication, QWidget
+from PySide6.QtWidgets import QWidget
 from helper.usesqapplication import UsesQApplication
 
 
@@ -32,7 +33,8 @@ class SqlDatabaseCreationDestructionAndQueries(UsesQApplication):
         # Acquire resources
         super().setUp()
         self.assertFalse(not QSqlDatabase.drivers(), "installed Qt has no DB drivers")
-        self.assertTrue("QSQLITE" in QSqlDatabase.drivers(), "\"QSQLITE\" driver not available in this Qt version")
+        self.assertTrue("QSQLITE" in QSqlDatabase.drivers(),
+                        "\"QSQLITE\" driver not available in this Qt version")
         self.db = QSqlDatabase.addDatabase("QSQLITE")
         self.db.setDatabaseName(":memory:")
         self.assertTrue(self.db.open())
@@ -57,7 +59,7 @@ class SqlDatabaseCreationDestructionAndQueries(UsesQApplication):
         # Test table creation, insertion and retrieval
         query = QSqlQuery()
         query.exec("CREATE TABLE person(id int primary key, "
-                    "firstname varchar(20), lastname varchar(20))")
+                   "firstname varchar(20), lastname varchar(20))")
         query.exec("INSERT INTO person VALUES(101, 'George', 'Harrison')")
         query.prepare("INSERT INTO person (id, firstname, lastname) "
                       "VALUES (:id, :firstname, :lastname)")
@@ -75,7 +77,7 @@ class SqlDatabaseCreationDestructionAndQueries(UsesQApplication):
 
     def testTableModelDeletion(self):
         bar = Foo()
-        model = bar.model
+        model = bar.model  # noqa: F841
         del bar
         # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
         gc.collect()
@@ -83,4 +85,3 @@ class SqlDatabaseCreationDestructionAndQueries(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 3999be20ea80174c5ee9a83d05cc9c4e941317cd..7d4646ab19c0fb5b20abf502075b15d760ae616a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QVariant::Type converter'''
 import os
index 7fe5fc1e6bee5a6ba375a8ddf7e798703ca44177..8552d5e8e2c75fbb55a534ac6708fec233acd187 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 9c52bfd59a73a9dffc68291536b88f8c768736e2..b7f346b2af6a4579bf4472bdf92b0fd850beb244 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -13,8 +14,7 @@ init_test_paths(False)
 
 from PySide6.QtCore import (QCoreApplication, QObject, QParallelAnimationGroup,
                             QTimer, SIGNAL)
-from PySide6.QtStateMachine import (QEventTransition, QFinalState, QState,
-                                    QStateMachine, QSignalTransition)
+from PySide6.QtStateMachine import QEventTransition, QFinalState, QState, QStateMachine
 
 
 def addStates(transition):
index 4a33141c8893db89d54a71f89d7b96ab846d600b..7827c9b6df0e4ee65c3353ad31e3e00aba57188e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -12,8 +13,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QCoreApplication, QObject, SIGNAL, QTimer
-from PySide6.QtStateMachine import (QEventTransition, QFinalState, QState,
-                                    QStateMachine, QSignalTransition)
+from PySide6.QtStateMachine import QFinalState, QState, QStateMachine, QSignalTransition
 
 
 class QStateTest(unittest.TestCase):
index 5e17b3a1f96d7e072be6f2aa0de75b0b3d6653d0..7849a004d0dcbef3bf8225df6b43d15b272b0996 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -11,8 +12,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import (QObject, QParallelAnimationGroup,
-                            QPropertyAnimation, QTimer, SIGNAL)
+from PySide6.QtCore import QParallelAnimationGroup, QTimer
 from PySide6.QtStateMachine import (QFinalState, QState, QStateMachine)
 
 from helper.usesqapplication import UsesQApplication
index 48f397282980bd706998432d9a9067c32252c884..273a31df2f1ee9bf7a016ccf3eb1f2a659bfe33c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index a06cefb370b4618e39b2e692486fe378ee5e7188..4d9bf472dec1d7b53fd492e30c08495267e1a09e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -44,4 +45,3 @@ class QSvgGeneratorTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index b4422001fd00d2c21f7ff5a0dbf3ab96db08c5a5..22d932ed601c6760d4b3b31a9ed1b3d237dab5f7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -20,13 +21,13 @@ class QSvgRendererTest(unittest.TestCase):
 
     def testLoad(self):
         tigerPath = os.path.join(os.path.dirname(__file__), 'tiger.svg')
-        app = QGuiApplication([])
+        app = QGuiApplication([])  # noqa: F841
 
         fromFile = QSvgRenderer(tigerPath)
         self.assertTrue(fromFile.isValid())
 
         tigerFile = QFile(tigerPath)
-        tigerFile.open(QFile.ReadOnly)
+        tigerFile.open(QFile.OpenModeFlag.ReadOnly)
         tigerData = tigerFile.readAll()
         fromContents = QSvgRenderer(tigerData)
         self.assertTrue(fromContents.isValid())
@@ -34,4 +35,3 @@ class QSvgRendererTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 552d6f49a6781f9575c5e93ecea938fa4d981e8c..28e693dc26e247e53d4fcfea7ea975f2af41a071 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -23,13 +24,13 @@ class QSvgWidgetTest(unittest.TestCase):
         tigerPath = QDir.cleanPath(f"{directory}/../QtSvg/tiger.svg")
         self.assertTrue(QFileInfo.exists(tigerPath))
 
-        app = QApplication([])
+        app = QApplication([])  # noqa: F841
         fromFile = QSvgWidget()
         fromFile.load(tigerPath)
         self.assertTrue(fromFile.renderer().isValid())
 
         tigerFile = QFile(tigerPath)
-        tigerFile.open(QFile.ReadOnly)
+        tigerFile.open(QFile.OpenModeFlag.ReadOnly)
         tigerData = tigerFile.readAll()
         fromContents = QSvgWidget()
         fromContents.load(tigerData)
@@ -38,4 +39,3 @@ class QSvgWidgetTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index da8b2dcf67c05cb3959deb0e7d3f0ceb779a9005..0d86c1704059fcc86b91049159b5ecd60ffcc682 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''QtTest mouse click functionalities'''
 
@@ -12,8 +13,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import Qt, QObject
-from PySide6.QtWidgets import QPushButton, QLineEdit
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QPushButton
 from PySide6.QtTest import QTest
 
 from helper.usesqapplication import UsesQApplication
@@ -27,10 +28,10 @@ class MouseClickTest(UsesQApplication):
         button.setCheckable(True)
         button.setChecked(False)
 
-        QTest.mouseClick(button, Qt.LeftButton)
+        QTest.mouseClick(button, Qt.MouseButton.LeftButton)
         self.assertTrue(button.isChecked())
 
-        QTest.mouseClick(button, Qt.LeftButton)
+        QTest.mouseClick(button, Qt.MouseButton.LeftButton)
         self.assertFalse(button.isChecked())
 
 
index c57974c0dc3a614e96ff657f8e64b28e3bc43324..80a8307e6296fbaa9d28fd7dbb4ad22919458810 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for QKeyEvent'''
 
@@ -32,8 +33,8 @@ class KeyEventFilter(QObject):
         self.processed = False
 
     def eventFilter(self, obj, event):
-        if self.widget == obj and event.type() == self.eventType and \
-               isinstance(event, QKeyEvent) and event.key() == self.key:
+        if (self.widget == obj and event.type() == self.eventType
+                and isinstance(event, QKeyEvent) and event.key() == self.key):
             self.processed = True
             return True
 
@@ -44,8 +45,8 @@ class EventFilterTest(UsesQApplication):
 
     def testKeyEvent(self):
         widget = QLineEdit()
-        key = Qt.Key_A
-        eventFilter = KeyEventFilter(widget, QEvent.KeyPress, key)
+        key = Qt.Key.Key_A
+        eventFilter = KeyEventFilter(widget, QEvent.Type.KeyPress, key)
         widget.installEventFilter(eventFilter)
 
         QTest.keyClick(widget, key)
index 06f05b4b8b449cebbcb53afb7bb5ca38b5b3ed66..11abb37e024f4015ae1cecf795e2dd52504a54bc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''QtTest QSignalSpy'''
 
@@ -12,9 +13,9 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import Qt, QObject, SIGNAL
+from PySide6.QtCore import SIGNAL
 from PySide6.QtGui import QStandardItem, QStandardItemModel
-from PySide6.QtTest import QTest, QSignalSpy
+from PySide6.QtTest import QSignalSpy
 
 from helper.usesqapplication import UsesQApplication
 
index 2382b860524dd5989d97c87ee5577ccabfb5d6d5..b362a27a215a255689e42173dc675e7f1763cef9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -22,7 +23,7 @@ class MyValidator1(QValidator):
         return "fixed"
 
     def validate(self, input, pos):
-        return (QValidator.Acceptable, "fixed", 1)
+        return (QValidator.State.Acceptable, "fixed", 1)
 
 
 class MyValidator2(QValidator):
@@ -30,7 +31,7 @@ class MyValidator2(QValidator):
         return "fixed"
 
     def validate(self, input, pos):
-        return (QValidator.Acceptable, "fixed")
+        return (QValidator.State.Acceptable, "fixed")
 
 
 class MyValidator3(QValidator):
@@ -38,7 +39,7 @@ class MyValidator3(QValidator):
         return "fixed"
 
     def validate(self, input, pos):
-        return (QValidator.Acceptable,)
+        return (QValidator.State.Acceptable,)
 
 
 class MyValidator4(QValidator):
@@ -46,15 +47,15 @@ class MyValidator4(QValidator):
         return "fixed"
 
     def validate(self, input, pos):
-        return QValidator.Acceptable
+        return QValidator.State.Acceptable
 
 
 class MyValidator5(QValidator):
     def validate(self, input, pos):
         if input.islower():
-            return (QValidator.Intermediate, input, pos)
+            return (QValidator.State.Intermediate, input, pos)
         else:
-            return (QValidator.Acceptable, input, pos)
+            return (QValidator.State.Acceptable, input, pos)
 
     def fixup(self, input):
         return "22"
@@ -114,7 +115,7 @@ class QValidatorTest(UsesQApplication):
         line.show()
         line.setValidator(MyValidator5())
         line.setText("foo")
-        QTest.keyClick(line, Qt.Key_Return)
+        QTest.keyClick(line, Qt.Key.Key_Return)
         self.assertEqual(line.text(), "22")
 
 
index bc07c25ed5724c5fab9d9e682ddfc0d5ef41eeaa..2e72ec00b9ff4dcbd6f8dd27676d3b794f8ca6cc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 6ff66c047869761306e5aba70814fda225c06c66..5624ba76b53bc47d7e01944710b70869a0516e8e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QTextToSpeech methods'''
 
@@ -16,10 +17,9 @@ init_test_paths(False)
 from helper.usesqapplication import UsesQApplication
 
 from PySide6.QtCore import QTimer
-from PySide6.QtWidgets import QApplication
 
 try:
-    from PySide6.QtTextToSpeech import QTextToSpeech, QVoice
+    from PySide6.QtTextToSpeech import QTextToSpeech
 except ImportError:
     print("Skipping test due to missing QtTextToSpeech module")
     sys.exit(0)
index 9eda559de894b25083f7fbd84dc573c5e714ba3b..47743b15c2a0cf4fd0319acbecb9c52d05ebd6d6 100644 (file)
@@ -1,16 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
-    <class>main_window</class>
-    <widget class="QMainWindow" name="main_window">
-      <widget class="QWidget" name="centralwidget"/>
-      <widget class="QMenuBar" name="menubar"/>
-      <widget class="QStatusBar" name="statusbar">
-        <action name="actionFoo">
-          <property name="text">
-            <string>foo</string>
-          </property>
-        </action>
-      </widget>
-    </widget>
-    <resources/>
-    <connections/>
+ <class>main_window</class>
+ <widget class="QMainWindow" name="main_window">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>16</width>
+    <height>69</height>
+   </rect>
+  </property>
+  <widget class="QWidget" name="centralwidget"/>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>16</width>
+     <height>26</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar">
+   <action name="actionFoo">
+    <property name="text">
+     <string>foo</string>
+    </property>
+   </action>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
 </ui>
index 52624a2a99fcfef7822938755dc3fca62ee1e76b..64522946e599612429ddf7ce6cb56d00853d60d7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' unit test for BUG #1060 '''
 
index 8ca764eae767d125c139efcc3f902bb653001589..956a7c6b452082759d32b34b4dc1ee74d57374bf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -37,7 +38,7 @@ class ButTest(UsesQApplication):
         loader = MyQUiLoader(w)
 
         filePath = os.path.join(os.path.dirname(__file__), 'minimal.ui')
-        ui = loader.load(filePath)
+        ui = loader.load(filePath)  # noqa: F841
 
         self.assertEqual(len(loader._widgets), 1)
         self.assertEqual(type(loader._widgets[0]), QFrame)
@@ -45,4 +46,3 @@ class ButTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index eefc1fe0566aca69e75a726380b180de83f2c4ec..e2079eb506e7e0241868f48acdba5f92ed2bda9c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -28,4 +29,3 @@ class BugTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 4d69fdbecc263d0ce0b5ea56a206c73639ee2477..a7463b874572d927d0549f1bd68f5af4046cdaad 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -60,4 +61,3 @@ class BugTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 3a568e4e2e7e2adfaa63506d108ebbffbb9272c1..d1dc7d9e3775317c02b9a768bb1a4b400a13fd59 100644 (file)
@@ -1,9 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
-import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
@@ -11,12 +11,12 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QTimer
-from PySide6.QtGui import QAction, QActionGroup
-from PySide6.QtWidgets import QApplication, QWidget
+from PySide6.QtGui import QActionGroup
+from PySide6.QtWidgets import QApplication
 from PySide6.QtUiTools import QUiLoader
 
 
-class Window(object):
+class Window:
     def __init__(self):
         loader = QUiLoader()
         filePath = os.path.join(os.path.dirname(__file__), 'bug_426.ui')
index 06a6b72a4e0655c1c728abac059239cf9b2b2d03..84d9d26c13cb12093f962b96c421cb390f380ce8 100644 (file)
@@ -1,9 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
-import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
@@ -11,7 +11,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QObject
-from PySide6.QtWidgets import QApplication, QFrame, QWidget
+from PySide6.QtWidgets import QApplication, QWidget
 from PySide6.QtUiTools import QUiLoader
 
 
index 3dae7afb7478eadf077d47b53f07fd7107c8497f..1df38f89a23a3dde9d30f0a1a005896a69911cfb 100644 (file)
@@ -1,9 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
-import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
index cd4d50a0bd172d02b547c448af059692fb31f6c6..e1a1df8f6bccf068e7ffd6d2af2b1c7b6e3c49cb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b07f62d05b45ec74a703fdb819c7af09b7e5a2cf..be9153883fb7130e987ec1f5474ffe54c2e80ca2 100644 (file)
@@ -20,7 +20,7 @@
      <x>0</x>
      <y>0</y>
      <width>800</width>
-     <height>25</height>
+     <height>26</height>
     </rect>
    </property>
   </widget>
index 85df29d11b1ce87e0d6ea03d9582e15fb7185ff7..47cbfd706d4ee9f629fa51275fe772f9008547d3 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 2f8dfab62d3f43411de1403be72d4cd6d2b1718c..3f0ba8d99617ca199a0a640c01ad96342783e255 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 2308cbeeded6e9353ceb37adc3513e1ee7746782..ee11f1d2c36addb6d2672ef6252a521d3de577de 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index e324db8292cb77b96e7b8b077e18a5df5a31de55..be9153883fb7130e987ec1f5474ffe54c2e80ca2 100644 (file)
@@ -1,13 +1,8 @@
-<ui version="4.0" >
- <author></author>
- <comment></comment>
- <exportmacro></exportmacro>
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
  <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow" >
-  <property name="objectName" >
-   <string notr="true" >MainWindow</string>
-  </property>
-  <property name="geometry" >
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <height>600</height>
    </rect>
   </property>
-  <property name="windowTitle" >
+  <property name="windowTitle">
    <string>MainWindow</string>
   </property>
-  <widget class="QMenuBar" name="menubar" />
-  <widget class="QWidget" name="centralwidget" />
-  <widget class="QStatusBar" name="statusbar" />
+  <widget class="QWidget" name="centralwidget"/>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>26</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
  </widget>
- <pixmapfunction></pixmapfunction>
+ <resources/>
  <connections/>
 </ui>
index b0842318ca0ab6afb87e9843018068aafb04dad6..3eddbd294e63d9e356f17b635800d1a2320fd4c1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -49,4 +50,3 @@ class loadUiTypeTester(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index c6bb70cdad8b9e1d30c2a5013a2f167bdd29d92d..c63aa81488b9fd46b38859db3a8e665b368a34ce 100644 (file)
@@ -2,5 +2,15 @@
 <ui version="4.0">
  <class>Form</class>
  <widget class="QFrame" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>100</width>
+    <height>30</height>
+   </rect>
+  </property>
  </widget>
+ <resources/>
+ <connections/>
 </ui>
index 8826ac1fbc550322b6bb5acb4ff2cd5b0b8713f7..e72d71da981febcb5560aa2286776772d156a0d6 100644 (file)
@@ -13,7 +13,6 @@
   <property name="windowTitle">
    <string/>
   </property>
-
   <widget class="MyWidget" name="custom">
    <property name="geometry">
     <rect>
@@ -24,8 +23,7 @@
     </rect>
    </property>
   </widget>
-
- <widget class="MyWidget" name="custom2">
+  <widget class="MyWidget" name="custom2">
    <property name="geometry">
     <rect>
      <x>10</x>
@@ -34,7 +32,7 @@
      <height>23</height>
     </rect>
    </property>
- </widget>
+  </widget>
  </widget>
  <customwidgets>
   <customwidget>
index 60afe22b359d18ad7878487ccb70fd3bcb20544c..2df30e8ffa6c312d2dc7e07c034851d0cac5b126 100644 (file)
     </rect>
    </property>
    <property name="frameShape">
-    <enum>QFrame::StyledPanel</enum>
+    <enum>QFrame::Shape::StyledPanel</enum>
    </property>
    <property name="frameShadow">
-    <enum>QFrame::Raised</enum>
+    <enum>QFrame::Shadow::Raised</enum>
    </property>
    <widget class="QPushButton" name="grandson_object">
     <property name="geometry">
index 4e2fa808b8a225ab70513525a35385efa3cd424d..f2fbea1a12c2556fcad336ae15031799eedf9153 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 19e26912e7830420a21c84e03c0f4d02ed74bfbc..c1bcf0e814af5c87f8c502a46dee0268920bcceb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -40,7 +41,8 @@ class QUiLoaderTester(UsesQApplication):
 
         child = w.findChild(QWidget, "child_object")
         self.assertNotEqual(child, None)
-        self.assertEqual(w.findChild(QWidget, "grandson_object"), child.findChild(QWidget, "grandson_object"))
+        self.assertEqual(w.findChild(QWidget, "grandson_object"),
+                         child.findChild(QWidget, "grandson_object"))
 
     def testLoadFileOverride(self):
         # PYSIDE-1070, override QUiLoader::createWidget() with parent=None crashes
@@ -51,4 +53,3 @@ class QUiLoaderTester(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index a30c72a8ec0d6b8d577b62b3cd527faf77156976..beadda0a55a773e16ea4adf10ae395e37e82119e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 import os
 import sys
 import unittest
index 10c8e88d75c43fc1fb71cd976376b03b23e8d064..47593e242c510b29946c62c9d823275aa229eb32 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -7,15 +8,14 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from PySide6.QtCore import QBuffer, Qt, QTimer
-from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout
-from PySide6.QtWebEngineWidgets import QWebEngineView
-from PySide6.QtWebEngineCore import (QWebEngineProfile,
-                                     QWebEngineUrlScheme,
-                                     QWebEngineUrlSchemeHandler)
+from PySide6.QtCore import QBuffer, Qt, QTimer  # noqa: E402
+from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout  # noqa: E402
+from PySide6.QtWebEngineWidgets import QWebEngineView  # noqa: E402
+from PySide6.QtWebEngineCore import (QWebEngineProfile, QWebEngineUrlScheme,  # noqa: E402
+                                     QWebEngineUrlSchemeHandler)  # noqa: E402
 
 
 class TestSchemeHandler(QWebEngineUrlSchemeHandler):
@@ -33,7 +33,7 @@ class TestSchemeHandler(QWebEngineUrlSchemeHandler):
 class MainTest(unittest.TestCase):
     def test_SchemeHandlerRedirect(self):
         self._loaded = False
-        QApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
+        QApplication.setAttribute(Qt.ApplicationAttribute.AA_ShareOpenGLContexts)
         app = QApplication([])
 
         scheme_name = bytes("testpy", "UTF-8")
index 3739e956a28e466fa9c0d40212bf248b1c8eddb2..09fac506793014bb4ad9a213c5f51f6d4069043d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 from functools import partial
 import os
@@ -9,13 +10,13 @@ import unittest
 TEST_DIR = os.path.dirname(os.path.abspath(__file__))
 
 sys.path.append(os.path.dirname(TEST_DIR))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from PySide6.QtCore import QCoreApplication, QSize, QUrl, Qt
-from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
-from PySide6.QtWebEngineCore import QWebEnginePage
-from PySide6.QtWebEngineWidgets import QWebEngineView
+from PySide6.QtCore import QCoreApplication, QSize, QUrl  # noqa: E402
+from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget  # noqa: E402
+from PySide6.QtWebEngineCore import QWebEnginePage  # noqa: E402
+from PySide6.QtWebEngineWidgets import QWebEngineView  # noqa: E402
 
 
 class MainTest(unittest.TestCase):
diff --git a/sources/pyside6/tests/QtWebView/CMakeLists.txt b/sources/pyside6/tests/QtWebView/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ccbf9ad
--- /dev/null
@@ -0,0 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+PYSIDE_TEST(test_webview.py)
+
diff --git a/sources/pyside6/tests/QtWebView/QtWebView.pyproject b/sources/pyside6/tests/QtWebView/QtWebView.pyproject
new file mode 100644 (file)
index 0000000..ccc8a68
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "files": ["test_webview.py"]
+}
diff --git a/sources/pyside6/tests/QtWebView/test_webview.py b/sources/pyside6/tests/QtWebView/test_webview.py
new file mode 100644 (file)
index 0000000..e8f4ecc
--- /dev/null
@@ -0,0 +1,33 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+'''Unit test for WebView'''
+
+import os
+import sys
+import unittest
+from pathlib import Path
+
+# Append the necessary paths to sys.path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from PySide6.QtWebView import QtWebView
+
+from helper.usesqapplication import UsesQApplication
+
+
+class QWebViewTestCase(UsesQApplication):
+    def test_webview_exists(self):
+        # Initialize QtWebView
+        QtWebView.initialize()
+
+        # Check if QtWebView can be initialized
+        self.assertTrue(QtWebView is not None)
+        print("QtWebView is available in PySide6.")
+
+
+if __name__ == "__main__":
+    unittest.main()
index 5563a4899c4eb4561bbca8beaf63853bbaa3aa29..c81871df4686f9d6e8d8b2ff7d0e9529290f43bf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 3ca1660a94dcd7d7ab0f115336ed3fbc9562286b..a6005bde0e7e7371730f77b381885c847aa0993c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for QMenuBar.addAction(identifier, callback) calls'''
 
@@ -39,8 +40,8 @@ class AddActionTest(UsesQApplication):
         '''QMenuBar.addAction(id, callback)'''
         menubar = QMenuBar()
         action = menubar.addAction("Accounts", self._callback)
-        action.activate(QAction.Trigger)
-        action.setShortcut(Qt.Key_A)
+        action.activate(QAction.ActionEvent.Trigger)
+        action.setShortcut(Qt.Key.Key_A)
         self.assertTrue(self.called)
 
     def testWithCppSlot(self):
@@ -50,11 +51,10 @@ class AddActionTest(UsesQApplication):
         widget.setCheckable(True)
         widget.setChecked(False)
         action = menubar.addAction("Accounts", widget, SLOT("toggle()"))
-        action.setShortcut(Qt.Key_A)
-        action.activate(QAction.Trigger)
+        action.setShortcut(Qt.Key.Key_A)
+        action.activate(QAction.ActionEvent.Trigger)
         self.assertTrue(widget.isChecked())
 
 
 if __name__ == '__main__':
     unittest.main()
-
index e38672016723ffddeae1a6f0e655dbfe56f610de..fd3bfd862776c755afb8cb4c459d0b2efa0f43c5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for PySide API2 support'''
 
@@ -42,9 +43,9 @@ class DoubleQObjectInheritanceTest(UsesQApplication):
 
         # QIntValidator methods
         state, string, number = obj.validate('Test', 0)
-        self.assertEqual(state, QValidator.Invalid)
+        self.assertEqual(state, QValidator.State.Invalid)
         state, string, number = obj.validate('33', 0)
-        self.assertEqual(state, QValidator.Acceptable)
+        self.assertEqual(state, QValidator.State.Acceptable)
 
     def testQSpinBox(self):
         obj = WidgetValidatorQSpinBox()
index 1b2477fd76f5562ef8ecfd2217b524649d2fc3e1..05d90abc83323fd7d22361c909b88574a7d835a4 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -13,7 +14,6 @@ init_test_paths(True)
 
 from testbinding import TestObject
 from PySide6.QtWidgets import QApplication
-from PySide6 import __all__ as all
 
 
 class QApplicationInstance(unittest.TestCase):
@@ -22,21 +22,15 @@ class QApplicationInstance(unittest.TestCase):
         self.assertTrue(False)
 
     def testInstanceObject(self):
-        self.assertEqual(type(qApp), type(None))
+        self.assertEqual(type(qApp), type(None))  # noqa: F821
         TestObject.createApp()
         app1 = QApplication.instance()
         app2 = QApplication.instance()
         app1.setObjectName("MyApp")
         self.assertEqual(app1, app2)
         self.assertEqual(app2.objectName(), app1.objectName())
-        # We no longer support qApp when embedding
-        # if len(all) > 3:
-        #     # an import triggers qApp initialization
-        #     __import__("PySide6." + all[-1])
-        #     self.assertEqual(app1, qApp)
         app1.destroyed.connect(self.appDestroyed)
 
 
 if __name__ == '__main__':
     unittest.main()
-
index 98ae66c47a603850d3d358589b543b582573c793..a8f836ce53515080762f10c6d6420a9e9278cc00 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 74bf5e2da70ef33fb805dd76ab6a272238df279c..9baf85def19d3c08eab231cd2d388d66d68b650e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -31,7 +32,7 @@ class LabelWindow(QDialog):
     def replace(self, unit):
         old_item = self.test_layout.itemAtPosition(0, 0)
         old_label = old_item.widget()
-        ref = weakref.ref(old_item, self._destroyed)
+        ref = weakref.ref(old_item, self._destroyed)  # noqa: F841
 
         self.test_layout.removeWidget(old_label)
         unit.assertRaises(RuntimeError, old_item.widget)
@@ -41,7 +42,7 @@ class LabelWindow(QDialog):
 
         label = QLabel("Label New")
         old_label.deleteLater()
-        label.setAlignment(Qt.AlignCenter)
+        label.setAlignment(Qt.AlignmentFlag.AlignCenter)
         self.test_layout.addWidget(label, 0, 0)
 
     def _destroyed(self, obj):
index 9d83e2f3077b353fcc2a1ce6b6f86cf40f82f00a..4edad18709c55ff4aeecda8217b52629798d8a64 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index c9559609fb0156c41baaf464e0356e0da8d455d9..bb2edd19ceb509a4d7ad914e3f463f9b9fa7d628 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' unit test for BUG #1077 '''
 
@@ -14,7 +15,7 @@ from init_paths import init_test_paths
 init_test_paths()
 
 from PySide6.QtGui import QSyntaxHighlighter
-from PySide6.QtWidgets import QApplication, QTextEdit, QWidget
+from PySide6.QtWidgets import QApplication, QTextEdit
 
 
 class Highlighter(QSyntaxHighlighter):
index 2637d6d614764776cc392272088f2d49ce53c070..6f2895e472a0d96ed355617e282686bd8b04c3dd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index af189837f00cd1391ffb95700c3ddac87ffbaf49..7987be96e69f0f5dffb4822516b31446aa13a77f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 243: http://bugs.openbossa.org/show_bug.cgi?id=243'''
 
index 673d5183d663ad24ce7709bead666951c8b6c760..8f59a81da10eb769fb9a9a7af51731b40df3bdba 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
-import colorsys
 import os
 import sys
 import unittest
index 695b4238aa031da122b0d662ba46612f60924240..463c20527fc52459d5e8e4efe9b0fd2cc616d158 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 338: http://bugs.openbossa.org/show_bug.cgi?id=338'''
 
index 024908e858913cec888955983c8cc6f825789fb2..60305a8b16b083dd65163be28848c32a65b69d94 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 389: http://bugs.openbossa.org/show_bug.cgi?id=389'''
 
@@ -20,7 +21,7 @@ from PySide6.QtWidgets import QStyle, QWidget
 class BugTest(UsesQApplication):
     def testCase(self):
         s = QWidget().style()
-        i = s.standardIcon(QStyle.SP_TitleBarMinButton)
+        i = s.standardIcon(QStyle.StandardPixmap.SP_TitleBarMinButton)
         self.assertEqual(type(i), QIcon)
 
 
index 79dfa49fca6a69caee606854e84735e8152ec5ee..da446fe04cb0675d9a0b162a658cac8840a85c84 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 400: http://bugs.openbossa.org/show_bug.cgi?id=400'''
 
index e95c113f3953b61915aa7b3c3c51f98082d70621..8b37d933251035c4f5b1bda222a9da2f4b0bc09b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 6ef9ec7f4fcf397fef431427a047d2fadea0b506..a0fac618979b59df1915c72c8b5b2decc8bb8e8b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index c7fbe4b3ee456877f0a6a57bcc2f9fad7a442792..5adcacccce57a2754914fd2f012f4b8e943469e9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index af383e8112a479bd1e364dfd386ff71210b22c82..a71a06c8dbf23037c0a7eb683e73435722518832 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 367: http://bugs.openbossa.org/show_bug.cgi?id=467'''
 
index 2fc9555616cb85a8f8632a2d8e8a1e91cbf7e3d7..10c19c482485e4a81b2cc64347057dac5cea4d54 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -27,7 +28,7 @@ class BuggyWidget(QWidget):
 
 class LayoutTransferOwnerShip(unittest.TestCase):
     def testBug(self):
-        app = QApplication([])
+        app = QApplication([])  # noqa: F841
         w = BuggyWidget()
         w.setup()
         w.show()
@@ -36,4 +37,3 @@ class LayoutTransferOwnerShip(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index dab367727951caf4707fe7bfc3b932b3e7db7885..e8cfecd600066baff402ed037cccc7ee8a6c138d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 512: http://bugs.openbossa.org/show_bug.cgi?id=512'''
 
index 3c49cd37769fac18eb2cabcd6a626ea932fd909d..7775ed1b6bc4c8788071b589020e9e8d3b20ab65 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index fcec2f6d78fa73d333bcbf891761e682bdfc42cc..c5e054c3ff622b8744eedcc49bf93e022ae0fd9c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index dcbe20ee85d5471f18c7c4463028395d736fff66..d4e25f60702cee8c0b5f860afd1e9481735e35dd 100644 (file)
@@ -1,10 +1,10 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """ Unittest for bug #547 """
 """ http://bugs.openbossa.org/show_bug.cgi?id=547 """
 
-import sys
 import os
 import sys
 import unittest
@@ -70,4 +70,3 @@ class MyMainWindow(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 8efa6e679ad07d4fa94788877c26b67b6db07525..41e23a7e9922394b43217c7ac059bc5e10d5236c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -15,7 +16,7 @@ from PySide6.QtWidgets import QApplication, QGraphicsWidget
 
 class TestBug549(unittest.TestCase):
     def testBug(self):
-        app = QApplication([])
+        app = QApplication([])  # noqa: F841
         w = QGraphicsWidget()
         w.setContentsMargins(1, 2, 3, 4)
         self.assertEqual(w.getContentsMargins(), (1, 2, 3, 4))
@@ -25,4 +26,3 @@ class TestBug549(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 0f95aed020ddc2e54459b06518daa178ff28fc81..4da978f6cfb2b1c574465306da1d3ee47087c964 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f9a927817e2d7526657823114a93768abae46c2e..3b2716dda0a9b35fca13a9ce3a39e1a8b49d96bd 100644 (file)
@@ -1,10 +1,10 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """ Unittest for bug #575 """
 """ http://bugs.openbossa.org/show_bug.cgi?id=575 """
 
-import sys
 import os
 import sys
 import unittest
@@ -19,7 +19,7 @@ from PySide6.QtWidgets import QApplication, QPlainTextEdit, QTextEdit
 
 class Bug575(unittest.TestCase):
     def testPropertyValues(self):
-        app = QApplication(sys.argv)
+        app = QApplication(sys.argv)  # noqa: F841
         textEdit = QPlainTextEdit()
         textEdit.insertPlainText("PySide INdT")
         selection = QTextEdit.ExtraSelection()
@@ -30,4 +30,3 @@ class Bug575(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 4a17e7aa87441230f22f979d45e62f05e3a7f019..376c15ee8c99a622cd6d89313104d2967a1f0071 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """ Unittest for bug #576 """
 """ http://bugs.openbossa.org/show_bug.cgi?id=576 """
@@ -26,7 +27,7 @@ class Bug576(unittest.TestCase):
     @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
     def testWidgetParent(self):
         self._destroyed = False
-        app = QApplication(sys.argv)
+        app = QApplication(sys.argv)  # noqa: F841
         w = QWidget()
 
         b = QPushButton("test")
@@ -46,4 +47,3 @@ class Bug576(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index e26ca9ce01d964d0064a70f9cc67280782e55ced..dbb64a1970261da4965c1de52f9655d4045ac3d2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test bug 585: http://bugs.openbossa.org/show_bug.cgi?id=585'''
 
@@ -18,11 +19,11 @@ from PySide6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
 class Bug585(unittest.TestCase):
     @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
     def testCase(self):
-        app = QApplication([])
+        app = QApplication([])  # noqa: F841
         self._tree = QTreeWidget()
         self._tree.setColumnCount(2)
         i1 = QTreeWidgetItem(self._tree, ['1', ])
-        i2 = QTreeWidgetItem(self._tree, ['2', ])
+        i2 = QTreeWidgetItem(self._tree, ['2', ])  # noqa: F841
         refCount = sys.getrefcount(i1)
 
         # this function return None
@@ -35,4 +36,3 @@ class Bug585(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index a19f544a58aa067489158427e475edffb72c0ef3..d6259b8654501346dec205fc34113ce011d47aa5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # trimmed down diagramscene.py to demonstrate crash in sizeHint()
 
index 66a8c58c68692d72332e9d4f020c7552aae4866b..6c997b276338aa8bb7b5568f5be3f60ca592b13c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 635: http://bugs.openbossa.org/show_bug.cgi?id=635'''
 
index b2620b541c77cc89b16078a579bab4cc82a6114e..c3e0ed83a32ae62720f06a8d95c607cfe035c38a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 1cf8390df2c6aa8d7dcb53595babfa26eec23d1b..370e9474b5c46a65980aacb8025c7c586b5e4c2c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index e3a3130d0c73f407612362e1f81f1e309bbb8778..36187e9aa6220df68335ad3c6251837ee70f3f1c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 662: http://bugs.openbossa.org/show_bug.cgi?id=662'''
 
index be6700d83c9e00a74e1f77e90524b79e1e928a52..1340dfba43303fe3cfcad6aee69a6d763308d5f9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 0c22436b34587ab9221973402435ec85631bf39b..0b2de8f043702789c54e983b5fec171e51a9f24e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 901b2927542e60dc73ec51f504984a2c1fab9ac2..27a00ad457f5d064020da2a1987d96bdb043967f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,7 +18,7 @@ class TestBug679(unittest.TestCase):
     '''QGraphicsScene::clear() is missing'''
     @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
     def testIt(self):
-        app = QApplication([])
+        app = QApplication([])  # noqa: F841
 
         scene = QGraphicsScene()
         hello = scene.addText("Hello")
@@ -32,4 +33,3 @@ class TestBug679(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 221df88b0cddb3d25b03ac6634337eeceb37ccf4..e94410420b47f891fa12655a9e1bcb7f39dc45d6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 3180d08bfdacc826121c77a44de7610bb708788e..db34f66915b0e5cf122c037418c52d269713b09e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test bug 688: http://bugs.openbossa.org/show_bug.cgi?id=688'''
 
@@ -13,7 +14,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
-from PySide6.QtGui import QTextFrame, QTextCursor, QTextCharFormat, QFont, QTextFrameFormat
+from PySide6.QtGui import QTextCursor, QTextCharFormat, QFont, QTextFrameFormat
 from PySide6.QtWidgets import QTextEdit
 
 
@@ -21,13 +22,13 @@ class BugTest(UsesQApplication):
     def testCase(self):
         editor = QTextEdit()
         cursor = QTextCursor(editor.textCursor())
-        cursor.movePosition(QTextCursor.Start)
+        cursor.movePosition(QTextCursor.MoveOperation.Start)
 
         mainFrame = cursor.currentFrame()
 
         plainCharFormat = QTextCharFormat()
         boldCharFormat = QTextCharFormat()
-        boldCharFormat.setFontWeight(QFont.Bold)
+        boldCharFormat.setFontWeight(QFont.Weight.Bold)
         cursor.insertText("""
                           Text documents are represented by the
                           QTextDocument class, rather than by QString objects.
index 95981e2721df3ac4d62eac60c261801a96665d27..23dd752ad2141df059558ab8a00fef876a60e5bd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 19e9978a68862183e06ccdd993983bba0f21e78c..6aee1074535bc3bcc832bfb3247edaf243377abb 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -12,7 +13,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
-from PySide6.QtWidgets import QMainWindow, QMenu, QApplication
+from PySide6.QtWidgets import QMainWindow, QMenu
 
 
 class MainWindow(QMainWindow):
@@ -35,4 +36,3 @@ class Bug696(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index fed330fb90a1750ea5e224a5a617ff110bfc38e6..ed6544ca3257abc14823ac68fee6a4a320dc4319 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -31,4 +32,3 @@ class TestLabelPixmap(unittest.TestCase):
 if __name__ == '__main__':
     app = QApplication([])
     unittest.main()
-
index dc20e14cf85c7f8f5d2ebe9fda518ff63a6494aa..fa3670ca6888074bf65f03bbf6dfbb57046139e2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 531adbb2570b212cbef785631b4897a3762f59a8..768c6bb950b04e6a81cb3aeb5ef4be56f13f2ac2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -35,5 +36,7 @@ timer.setInterval(50)
 timer.timeout.connect(timerHandler)
 timer.start()
 
-# This test for a dead lock in QFileDialog.getOpenFileNames, the test fail with a timeout if the dead lock exists.
-QFileDialog.getOpenFileNames(None, "caption", QDir.homePath(), None, "", QFileDialog.DontUseNativeDialog)
+# This test for a dead lock in QFileDialog.getOpenFileNames,
+# the test fail with a timeout if the dead lock exists.
+QFileDialog.getOpenFileNames(None, "caption", QDir.homePath(), None, "",
+                             QFileDialog.Option.DontUseNativeDialog)
index af4bcbda8edcc13cc499a1abd57cb69dca265c2c..133a8001fc43755a5364ebebaa54e345be81c1dd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -16,9 +17,9 @@ from PySide6.QtWidgets import QApplication, QSlider
 class TestBug736 (unittest.TestCase):
 
     def testIt(self):
-        app = QApplication([])
-        slider = QSlider(Qt.Horizontal)
-        slider2 = QSlider(Qt.Horizontal)
+        app = QApplication([])  # noqa: F841
+        slider = QSlider(Qt.Orientation.Horizontal)
+        slider2 = QSlider(Qt.Orientation.Horizontal)
 
         slider2.setMaximum(10)
         slider.valueChanged[int].connect(slider2.setMaximum)
index 999f6a3db0801ef4fe6be6ecfb5eea5303fad80f..074f569f6762e254ad37abd0b45d02aefa3c2ef3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -13,7 +14,7 @@ init_test_paths(False)
 from helper.usesqapplication import UsesQApplication
 
 from PySide6.QtCore import QTimer
-from PySide6.QtGui import QPainter, QFont, QFontInfo
+from PySide6.QtGui import QPainter
 from PySide6.QtWidgets import QWidget
 
 
index 1a7def6c7fb3d74f6078c92f89ff54b649bf07ab..dcbf177c44fd1f1d8c3fc479b89821cbf98af35e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 94f42a5d316cef9249afe281b8875cd908f9fa76..3e79006792d8d09535ac0ef9032e5d64c4ed2427 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b3537e1f7f04595f5927b862da9d2e82497288f2..3e6ceb7b073ab5aa20d2d67ee26b05242c1c0764 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 685f17e56f8a788948b8bf443495d1544e03102a..e49760a9d880a94e7a4b8557750df30a970d8762 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -24,8 +25,8 @@ window = Window()
 
 dock1 = QDockWidget()
 dock2 = QDockWidget()
-window.addDockWidget(Qt.LeftDockWidgetArea, dock1)
-window.addDockWidget(Qt.LeftDockWidgetArea, dock2)
+window.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock1)
+window.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock2)
 window.tabifyDockWidget(dock1, dock2)
 
 window.show()
index c2d3d560905d7606ec6e8782adc7471682369394..9486233e308792967db9909e0be7e626e0cfb715 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -14,15 +15,15 @@ from PySide6.QtCore import QTimer
 from PySide6.QtWidgets import QApplication, QFrame
 
 
-class Mixin1(object):
+class Mixin1:
     pass
 
 
-class Mixin2(object):
+class Mixin2:
     pass
 
 
-class Mixin3(object):
+class Mixin3:
     pass
 
 
@@ -42,5 +43,3 @@ def main():
 
 if __name__ == "__main__":
     main()
-
-
index 913b4235ce76ce7462c7cd6bb891ccebae4f99b0..271204e5b240316cbbfeec09fd43077b476d043f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import sys
 import os
index 1dbfd4b3a10529cd64ac9f0a1a94bc64f6da2d68..6f6c9a2bc56a5fdc6342a608fbda542ec0285140 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtWidgets import QTableView, QApplication
+from PySide6.QtWidgets import QTableView
 from PySide6.QtCore import QAbstractItemModel, QModelIndex
 
 from helper.usesqapplication import UsesQApplication
@@ -51,4 +52,3 @@ class TestQAbstractItemModel(UsesQApplication):
 
 if __name__ == "__main__":
     unittest.main()
-
index 779feaa147d5d690ca633fde0f4fb6720e928463..6b47ed13654c943107d99704e954db061f9b05e8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 49d9e24b27a012886fa17c8b29761d1233c51146..eb2c587118694f837d8c469effd4e2891ce00853 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 
 #
index 4ee9b575a7e10a96da3fc47bb113e4de592de340..55cb3283b758b503284322a82d4e4d2ca9cca2bc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -50,4 +51,3 @@ class Bug871Test(UsesQApplication):
 
 if __name__ == "__main__":
     unittest.main()
-
index 31c65654339905c123cf61de1cb3ed6f76e81f51..acd0196c93d798c762bbf045a9b18a9ce02db09d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -35,7 +36,8 @@ class TestBug879 (unittest.TestCase):
         self.assertEqual(self.box.text(), '0')
 
     def sendKbdEvent(self):
-        ev = QKeyEvent(QEvent.KeyPress, Qt.Key_A, Qt.NoModifier, 'a')
+        ev = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_A,
+                       Qt.KeyboardModifier.NoModifier, 'a')
         QCoreApplication.sendEvent(self.box, ev)
 
 
index 67387ed269d0c9aa4d2e88bc6d75a33065fb8b82..6f2fc128dbdc6958e479ac2a0f07796d395c58f9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -28,7 +29,7 @@ class MyWidget(QPushButton):
         p = QPainter(self)
         style = QApplication.style()
         option = QStyleOptionButton()
-        style.drawControl(QStyle.CE_PushButton, option, p)
+        style.drawControl(QStyle.ControlElement.CE_PushButton, option, p)
         self._painted = True
         QTimer.singleShot(0, self._emitPainted)
 
index dafc1faef9b62ff967e378bba266f476e54ddca8..bdd18daf5bb746376a97bea26eedec08a19000da 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -23,11 +24,11 @@ class Signaller(QObject):
     s3 = Signal()
 
 
-class Window(object):
+class Window:
 
     def __init__(self, s):
         self._window = QMainWindow()
-        self._window.setAttribute(Qt.WA_DeleteOnClose, True)
+        self._window.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
         self._window.setWindowTitle("Demo!")
 
         self._s = s
index 336aa2f58f8e934edc865aff6d10edb8402c7d09..f5972d335727eeda714d5629697b5cc07de851ef 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -21,10 +22,11 @@ def foo(a, b):
 class TestBug941 (unittest.TestCase):
 
     def testIt(self):
-        app = QApplication([])
-        view = QHeaderView(Qt.Horizontal)
+        app = QApplication([])  # noqa: F841
+        view = QHeaderView(Qt.Orientation.Horizontal)
         self.assertTrue(view.sortIndicatorChanged.connect(foo))
-        view.sortIndicatorChanged.emit(0, Qt.Vertical)  # this can't raise an exception!
+        # this can't raise an exception!
+        view.sortIndicatorChanged.emit(0, Qt.Orientation.Vertical)
 
 
 if __name__ == '__main__':
index 86bbddf16a7aa848925dbd766b7679f90ff44fc7..f018d4af9034165ce51db92acce9cd3acce38f00 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,12 +18,13 @@ from PySide6.QtWidgets import QAbstractItemView, QApplication, QListView
 class TestBug964 (unittest.TestCase):
 
     def testIt(self):
-        app = QApplication([])
+        app = QApplication([])  # noqa: F841
         model = QStringListModel(["1", "2"])
         view = QListView()
         view.setModel(model)
         view.setCurrentIndex(model.index(0, 0))
-        newCursor = view.moveCursor(QAbstractItemView.MoveDown, Qt.NoModifier)
+        newCursor = view.moveCursor(QAbstractItemView.CursorAction.MoveDown,
+                                    Qt.KeyboardModifier.NoModifier)
         self.assertEqual(newCursor.row(), 1)
         self.assertEqual(newCursor.column(), 0)
 
index 03af030ca5d3ba48d7efb5bc88b5cebde5d2cdaa..d254f2877fdd3f1ea13bc327cb01c52e26fcab32 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 895cea12825f496c406d5b2417dc33ea3fb5ee96..77c16f1caeea32b4b7bd9c984c4f34d94d449441 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -11,7 +12,8 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QSizeF
-from PySide6.QtWidgets import QGraphicsProxyWidget, QSizePolicy, QPushButton, QGraphicsScene, QGraphicsView
+from PySide6.QtWidgets import (QGraphicsProxyWidget, QSizePolicy, QPushButton,
+                               QGraphicsScene, QGraphicsView)
 
 from helper.timedqapplication import TimedQApplication
 
@@ -23,7 +25,7 @@ def createItem(minimum, preferred, maximum, name):
     w.setMinimumSize(minimum)
     w.setPreferredSize(preferred)
     w.setMaximumSize(maximum)
-    w.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+    w.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
 
     return w
 
@@ -38,10 +40,10 @@ class TestBug972 (TimedQApplication):
         prefSize = QSizeF(210, 100)
         maxSize = QSizeF(300, 100)
 
-        a = createItem(minSize, prefSize, maxSize, "A")
-        b = createItem(minSize, prefSize, maxSize, "B")
-        c = createItem(minSize, prefSize, maxSize, "C")
-        d = createItem(minSize, prefSize, maxSize, "D")
+        a = createItem(minSize, prefSize, maxSize, "A")  # noqa: F841
+        b = createItem(minSize, prefSize, maxSize, "B")  # noqa: F841
+        c = createItem(minSize, prefSize, maxSize, "C")  # noqa: F841
+        d = createItem(minSize, prefSize, maxSize, "D")  # noqa: F841
 
         view = QGraphicsView(scene)
         view.show()
index a6c17385ed0adfa3a3a95adb2f6b7b9a10d207ea..0e780c044bc2a7be0279b7a1e312a365880535f8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f82ba6d27f3349a183603c4fcee81a19b9b17239..0fd582206e85d8c629b6a6b173e7ff2ddbc4f54e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 0f6311515c21d30ac11225d1f307bf09ca5739a9..d2d236dbb510588059dd15e859ce243e36fcda20 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f7a1a1b1ddbb25e0eddb759e41b2faaccac266a0..9e1ec6dacf262eca88b9aabe7a83fef8f9d051af 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -26,22 +27,22 @@ class CustomProxyWidgetTest(UsesQApplication):
     def testCustomProxyWidget(self):
         scene = QGraphicsScene()
 
-        proxy = CustomProxy(None, Qt.Window)
+        proxy = CustomProxy(None, Qt.WindowType.Window)
         widget = QLabel('Widget')
         proxy.setWidget(widget)
-        proxy.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+        proxy.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
         scene.addItem(proxy)
         scene.setSceneRect(scene.itemsBoundingRect())
 
         view = QGraphicsView(scene)
-        view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
-        view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+        view.setRenderHints(QPainter.RenderHint.Antialiasing
+                            | QPainter.RenderHint.SmoothPixmapTransform)
+        view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
         view.show()
 
-        timer = QTimer.singleShot(100, self.app.quit)
+        QTimer.singleShot(100, self.app.quit)
         self.app.exec()
 
 
 if __name__ == '__main__':
     unittest.main()
-
index ef848e22dc6c8c5c00d3962d0f7d389aa368cdc4..5cdc5c5afaeda6c149727ca7616f13c43ceefd85 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for calling methods further than the direct parent'''
 
index f12cceb5a14089b7e8e98ca1bc0ca2ae4bf201f5..1aa26acc48a6f08308a077fe097a18069c4e8f7e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for __hash__'''
 
@@ -27,4 +28,3 @@ class HashableTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 0b60241f0441d715bc7f3205309f313c112d7e45..cb810c02554c7249bca6bef2052b42ab406d0031 100644 (file)
@@ -1,2 +1,2 @@
-class PysideImportTest2(object):
+class PysideImportTest2:
     pass
index 9d21c4580345a4f9ea92a047373766967a30995d..f93770aa03d719064099c4888b78cb6b18a02399 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -83,9 +84,8 @@ class KeepReferenceTest(UsesQApplication):
             view.setModel(model)
         view = QTableView()
         createModelAndSetToView(view)
-        model = view.model()
+        model = view.model()  # noqa: F841
 
 
 if __name__ == '__main__':
     unittest.main()
-
index 076c9e7e1ae19dfad1d638d99411b1d254707d2d..904bdd894be4b15d90038a434b38a70fab84e9db 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''(Very) Simple test case for missing names from QtGui and QtWidgets'''
 
index d995c2a372b59c7ea3324651f539158b5a9158b1..fc54caee9b793f193d9b038491f067a86c193fcf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test paint event override in python'''
 
@@ -8,15 +9,13 @@ import os
 import sys
 import unittest
 
-from textwrap import dedent
-
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QTimer
-from PySide6.QtWidgets import QApplication, QWidget
+from PySide6.QtWidgets import QWidget
 
 from helper.usesqapplication import UsesQApplication
 
index 0ab09eead36fcbcaebc47e87de267bee3a736b5b..9fa0fdad61df0c8782114990a628b9093877b612 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,6 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject, QTimer, QThread
 from PySide6.QtWidgets import QTableView, QWidget
 
 
index 67ad14d68b8aa4d6d13cf8fbd34cea3993ebadcb..a6b2574d2933dec620c350f818ad062a086d007c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 This is the example from https://bugreports.qt.io/browse/PYSIDE-772
@@ -16,8 +17,9 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import Signal
-from PySide6.QtWidgets import QApplication, QWidget
-from PySide6 import QtWidgets
+from PySide6.QtWidgets import QApplication, QWidget  # noqa F401
+
+from helper.usesqapplication import UsesQApplication
 
 
 class Harness(QWidget):
@@ -58,13 +60,7 @@ class _Under(QWidget):
         self.method___result = self.sender()
 
 
-class TestMangle(unittest.TestCase):
-
-    def setUp(self):
-        QApplication()
-
-    def tearDown(self):
-        qApp.shutdown()
+class TestMangle(UsesQApplication):
 
     def testPrivateMangle(self):
         harness = Harness()
index ee8a3801409b95b8c37325d92d657cecb3e9fdb0..478031aea242ea5625c8d40d6baccae0dbcc97cb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 1ae3b0a35e9ad8b1dfd538e0deea9f5c2da26d1a..3431eb4063dd88dd5c879cacf0e147469b4da7b1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -29,7 +30,7 @@ class Foo(QPyTextObject):
 
 class QAbstractTextDocumentLayoutTest(UsesQApplication):
 
-    objectType = QTextFormat.UserObject + 1
+    objectType = QTextFormat.ObjectTypes.UserObject + 1
 
     def foo(self):
         fmt = QTextCharFormat()
@@ -46,7 +47,8 @@ class QAbstractTextDocumentLayoutTest(UsesQApplication):
         self.textEdit.show()
 
         interface = Foo()
-        self.textEdit.document().documentLayout().registerHandler(QAbstractTextDocumentLayoutTest.objectType, interface)
+        self.textEdit.document().documentLayout().registerHandler(
+            QAbstractTextDocumentLayoutTest.objectType, interface)
 
         QTimer.singleShot(0, self.foo)
         self.app.exec()
@@ -56,4 +58,3 @@ class QAbstractTextDocumentLayoutTest(UsesQApplication):
 
 if __name__ == "__main__":
     unittest.main()
-
index 021cf9c6b797292962c6fc9aeaf05d14561f8944..5fdb64d01401575239c5067d8893bcc56d334373 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QAccessible::installFactory().'''
 
@@ -39,7 +40,7 @@ class LineEditAccessible(QAccessibleInterface):
         return None
 
     def backgroundColor(self):
-        return QColor(Qt.white)
+        return QColor(Qt.GlobalColor.white)
 
     def child(self, index):
         return None
@@ -54,7 +55,7 @@ class LineEditAccessible(QAccessibleInterface):
         return None
 
     def foregroundColor(self):
-        return QColor(Qt.black)
+        return QColor(Qt.GlobalColor.black)
 
     def indexOfChild(self, child):
         return -1
@@ -124,7 +125,7 @@ class QAccessibleTest(UsesQApplication):
     def setUp(self):
         super().setUp()
         QAccessible.installFactory(accessible_factory)
-        window = Window()
+        window = Window()  # noqa: F841
 
     def testLineEdits(self):
         window = Window()
index a0049421fb4da393fa8a278e3e67c0501d3d166d..e6092df0cfb547bd0d289ff0bac41a83d785118d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -62,4 +63,3 @@ class SetShortcutTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 0b5453af5c7f848f3a52de0c15a9e3936596d754..ec8a4756931f369070a9f2cb2693a1cc38899817 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 The bug was caused by this commit:
index 5a66b8e337536598e25ebcd5e0088aa0fffe560f..86e7c60ddc4cd8fd40a5dfa8f57d5f3e0ade09c4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test the presence of qApp Macro'''
 
index 8bb47bc1c41cdef274f2b9253631f6869bcdb259..d6200e8ebb3929c8a1896042d2be70bb0c7a7ba3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -11,7 +12,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QSpinBox
+from PySide6.QtWidgets import QApplication, QPushButton
 
 
 class QApplicationDelete(unittest.TestCase):
@@ -20,7 +21,7 @@ class QApplicationDelete(unittest.TestCase):
     def testQPushButton(self):
         # QApplication deleted before QPushButton
         a = QApplication([])
-        b = QPushButton('aaaa')
+        b = QPushButton('aaaa')  # noqa: F841
         del a
         # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
         gc.collect()
index 7c5b61fd99ae64f777806e550677e588564a26ef..47a3c43715ad2a275d15700c1dd0e268095314d4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f63a9486f2cb01a70e4576fe3769e5d79fd83819..90c96372db1e33f52fd4030d6d2f5b44df8a9281 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index cb85ce7e21f9ae001fd615cec01db8c95524ad71..6d36dfb2065e8e902c828b9d51b640eadbc0775e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 83d6af3836ed489ca342eef25bdece94d56282f1..4ebaddf171bab92a1999b1c7170856dd55a07cf4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -8,13 +9,13 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
-from PySide6.QtCore import QObject
-from PySide6.QtWidgets import QInputDialog
+from PySide6.QtCore import QObject  # noqa: E402
+from PySide6.QtWidgets import QInputDialog  # noqa: E402
 
-from helper.usesqapplication import UsesQApplication
+from helper.usesqapplication import UsesQApplication  # noqa: E402
 
 
 class DynamicSignalTest(UsesQApplication):
@@ -24,7 +25,7 @@ class DynamicSignalTest(UsesQApplication):
 
     def testQDialog(self):
         dlg = QInputDialog()
-        dlg.setInputMode(QInputDialog.TextInput)
+        dlg.setInputMode(QInputDialog.InputMode.TextInput)
         lst = dlg.children()
         self.assertTrue(len(lst))
         obj = lst[0]
index 76432973258e421fd93bfc51e5b692ac94695080..dd4209d1047c01761a1e7893ac90635bdd221ba9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f87ea6935677e194c5134d4023aa01a1f6274153..3d0faf64e3aeaaebda4d6732245a10e171493a4a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -31,7 +32,7 @@ class QFormLayoutTest(UsesQApplication):
         self.assertTrue(isinstance(row, int))
         self.assertTrue(isinstance(role, QFormLayout.ItemRole))
         self.assertEqual(row, 0)
-        self.assertEqual(role, QFormLayout.SpanningRole)
+        self.assertEqual(role, QFormLayout.ItemRole.SpanningRole)
 
     def testGetWidgetPosition(self):
         formlayout = QFormLayout()
@@ -47,7 +48,7 @@ class QFormLayoutTest(UsesQApplication):
         self.assertTrue(isinstance(row, int))
         self.assertTrue(isinstance(role, QFormLayout.ItemRole))
         self.assertEqual(row, 0)
-        self.assertEqual(role, QFormLayout.SpanningRole)
+        self.assertEqual(role, QFormLayout.ItemRole.SpanningRole)
 
     def testGetLayoutPosition(self):
         formlayout = QFormLayout()
@@ -63,7 +64,7 @@ class QFormLayoutTest(UsesQApplication):
         self.assertTrue(isinstance(row, int))
         self.assertTrue(isinstance(role, QFormLayout.ItemRole))
         self.assertEqual(row, 0)
-        self.assertEqual(role, QFormLayout.SpanningRole)
+        self.assertEqual(role, QFormLayout.ItemRole.SpanningRole)
 
     def testTakeRow(self):
         window = QMainWindow()
index cb4115b55e409087fa1e05acf2eeb36666e4786e..4bbe6b0eb39970810c460761ebef54211a1f857e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 1114ad4feca5dd413a4a34c0addd33012f7bfcf2..eb366e3c04849fa1f03d0586969e299259cc7127 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test cases related to QGraphicsItem and subclasses'''
 
index a3d28b4d30b28756a32cdd61d14938c5e10856d7..71aba9941e137342d9c9fa6dd5796b08b9da7ef4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test cases related to QGraphicsItem and subclasses'''
 
index dee616bbe9ab238747e4c1817a7fad414332bb83..6a8123261ffeab6557744e3fd2c9c71124f5528a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -21,22 +22,22 @@ class QGraphicsProxyWidgetTest(UsesQApplication):
     def testQGraphicsProxyWidget(self):
         scene = QGraphicsScene()
 
-        proxy = QGraphicsProxyWidget(None, Qt.Window)
+        proxy = QGraphicsProxyWidget(None, Qt.WindowType.Window)
         widget = QLabel('Widget')
         proxy.setWidget(widget)
-        proxy.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+        proxy.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
         scene.addItem(proxy)
         scene.setSceneRect(scene.itemsBoundingRect())
 
         view = QGraphicsView(scene)
-        view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
-        view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+        view.setRenderHints(QPainter.RenderHint.Antialiasing
+                            | QPainter.RenderHint.SmoothPixmapTransform)
+        view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
         view.show()
 
-        timer = QTimer.singleShot(100, self.app.quit)
+        QTimer.singleShot(100, self.app.quit)
         self.app.exec()
 
 
 if __name__ == '__main__':
     unittest.main()
-
index 86604f27be1dc2d638f52fd8c17038493c73758a..0bfc2bb6952e0d5889f4477436fda4211fc909a5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Basic test cases for QGraphicsScene'''
 
@@ -14,7 +15,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import QPointF
-from PySide6.QtGui import QPolygonF, QPixmap, QPainterPath, QTransform, QWindow
+from PySide6.QtGui import QPolygonF, QPixmap, QPainterPath, QTransform
 from PySide6.QtWidgets import QApplication, QPushButton
 from PySide6.QtWidgets import QGraphicsScene
 from PySide6.QtWidgets import QGraphicsEllipseItem, QGraphicsLineItem
index 260653ae7d724bdb9335e287acd8ead8c0014d57..fa6455f14d81a3fc064fd8a44050921eef5069e6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -44,4 +45,3 @@ class TestInputDialog(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index bcb4c7a61dee70941a94e49c21b0128fdb14439b..4936260ef4ac2b8dc752dd89d85de89a12696f29 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QKeySequenceEdit.'''
 
index aba90b7b85e8e1ed91298e4b0b2d059450ef93b7..5f60d05682516efa05f139fd256050b655789d30 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QLabel'''
 
index df9bd7f904bbb9c3e36e83f9a8430ee2536a1180..660addf4ec4e9ba562aaffbfd4f944c6fd63dd15 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QLayout handling of child widgets references'''
 
@@ -15,7 +16,7 @@ init_test_paths(False)
 
 from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QGridLayout, QWidget
 from PySide6.QtWidgets import QStackedLayout, QFormLayout
-from PySide6.QtWidgets import QApplication, QPushButton, QLabel
+from PySide6.QtWidgets import QPushButton, QLabel
 
 from helper.usesqapplication import UsesQApplication
 
@@ -57,13 +58,13 @@ class SaveReference(UsesQApplication):
 
     @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
     def testMoveLayout(self):
-        l = QHBoxLayout()
+        layout = QHBoxLayout()
         self.assertEqual(sys.getrefcount(self.widget1), 2)
-        l.addWidget(self.widget1)
+        layout.addWidget(self.widget1)
         self.assertEqual(sys.getrefcount(self.widget1), 3)
 
         w = QWidget()
-        w.setLayout(l)
+        w.setLayout(layout)
         self.assertEqual(sys.getrefcount(self.widget1), 3)
 
     def testHBoxReference(self):
index cc41f78b15c456059aefc74498b09c32fce0898f..26449c34636d8a4c8edf7fb9f7ea29fdbf235564 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -68,32 +69,32 @@ class QLayoutTest(UsesQApplication):
     @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
     def testOwnershipTransfer(self):
         b = QPushButton("teste")
-        l = MyLayout()
+        layout = MyLayout()
 
-        l.addWidget(b)
+        layout.addWidget(b)
 
         self.assertEqual(sys.getrefcount(b), 2)
 
         w = QWidget()
 
         # transfer ref
-        w.setLayout(l)
+        w.setLayout(layout)
 
         self.assertEqual(sys.getrefcount(b), 3)
 
     @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
     def testReferenceTransfer(self):
         b = QPushButton("teste")
-        l = QHBoxLayout()
+        layout = QHBoxLayout()
 
         # keep ref
-        l.addWidget(b)
+        layout.addWidget(b)
         self.assertEqual(sys.getrefcount(b), 3)
 
         w = QWidget()
 
         # transfer ref
-        w.setLayout(l)
+        w.setLayout(layout)
 
         self.assertEqual(sys.getrefcount(b), 3)
 
@@ -105,17 +106,17 @@ class QLayoutTest(UsesQApplication):
     def testMissingFunctions(self):
         w = QWidget()
         b = QPushButton("test")
-        l = MissingItemAtLayout()
+        layout = MissingItemAtLayout()
 
-        l.addWidget(b)
+        layout.addWidget(b)
 
-        self.assertRaises(RuntimeError, w.setLayout, l)
+        self.assertRaises(RuntimeError, w.setLayout, layout)
 
     def testQFormLayout(self):
         w = QWidget()
         formLayout = QFormLayout()
         spacer = QSpacerItem(100, 30)
-        formLayout.setItem(0, QFormLayout.SpanningRole, spacer)
+        formLayout.setItem(0, QFormLayout.ItemRole.SpanningRole, spacer)
         w.setLayout(formLayout)
         w.show()
         QTimer.singleShot(10, w.close)
index bc186f755d14b0e2b99997a2f135c66d389f678e..3bc0b5a3c5c9c751e3b25c8882ebc90bd7f63052 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 377e63ffbf404fc89544955015867effd79cbac3..747fac7dd98cafb81057be073e800e1144f5dbe2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -23,7 +24,7 @@ class QListWidgetTest(UsesQApplication):
         o.setObjectName("obj")
 
         item = QListWidgetItem("item0")
-        item.setData(Qt.UserRole, o)
+        item.setData(Qt.ItemDataRole.UserRole, o)
         # item._data = o
         self.assertTrue(sys.getrefcount(o), 3)
         self.assertTrue(sys.getrefcount(item), 2)
@@ -58,7 +59,7 @@ class QListWidgetTest(UsesQApplication):
     def testIt(self):
         lst = QListWidget()
         lst.show()
-        slot = lambda: lst.removeItemWidget(lst.currentItem())
+        slot = lambda: lst.removeItemWidget(lst.currentItem())  # noqa: E731
         lst.addItem(QListWidgetItem("foo"))
         QTimer.singleShot(0, slot)
         QTimer.singleShot(0, lst.close)
index a85c1b48f03c1985df4da8a98851a0fc4df55a41..9bfc81d652960bc39a68b2032b2dcf32641c316f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 2f245c8ff37a6f22c3a6a8ebdc7387d81f1ca5ca..7d73aca574e8dc18021a66d11c9ade24bacc6e55 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -55,7 +56,7 @@ class TestMainWindow(UsesQApplication):
         self.assertEqual(sys.getrefcount(c), 2)
         w.setCentralWidget(c)
         self.assertEqual(sys.getrefcount(c), 3)
-        wr = weakref.ref(c, self.objDel)
+        wr = weakref.ref(c, self.objDel)  # noqa: F841
         w.setCentralWidget(None)
         c = None
         self.app.exec()
@@ -72,7 +73,7 @@ class TestMainWindow(UsesQApplication):
         w.setCentralWidget(c2)
         self.assertEqual(sys.getrefcount(c2), 3)
 
-        wr = weakref.ref(c, self.objDel)
+        wr = weakref.ref(c, self.objDel)  # noqa: F841
         w.setCentralWidget(None)
         c = None
 
@@ -94,4 +95,3 @@ class TestMainWindow(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index a6976a637977b4e34238eaca61d1ec966f0f6e2c..7d1d262e4352cb467f482b60b617339be2e24f25 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 5b42e755f7f1cd194f7b7c5b420b6cefcd8f2cf3..b7138aaf7dbc2e83befb0b54340f8f5324d23710 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test the QMenu.addAction() method'''
 
index d90961495fffaaa09d0238238f6435d71318e091..ddf8e804057017c83f3e0ced47646b2ace65cf8d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for multiple inheritance from 2 QObjects'''
 
@@ -12,7 +13,6 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject
 from PySide6.QtGui import QIntValidator, QValidator
 from PySide6.QtWidgets import QWidget
 
@@ -42,9 +42,9 @@ class DoubleQObjectInheritanceTest(UsesQApplication):
 
         # QIntValidator methods
         state, string, number = obj.validate('aaaa', 0)
-        self.assertEqual(state, QValidator.Invalid)
+        self.assertEqual(state, QValidator.State.Invalid)
         state, string, number = obj.validate('33', 0)
-        self.assertEqual(state, QValidator.Acceptable)
+        self.assertEqual(state, QValidator.State.Acceptable)
 
 
 if __name__ == '__main__':
index b81713b57931fd608cfbafbf46940b5e76dd7297..884f391a9b43ecb877aed47e4ebbb911b38c2eb5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -45,4 +46,3 @@ class QPictureTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index bfc04130e27e9607284563ce1dc294d33bbc7a20..e8f8992966cbe6f413bf385710dad1592bec7398 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -33,7 +34,7 @@ class QPushButtonTest(UsesQApplication):
         w = MyWidget()
         w.show()
 
-        timer = QTimer.singleShot(100, self.app.quit)
+        QTimer.singleShot(100, self.app.quit)
         self.app.exec()
 
     def buttonCb(self, checked):
@@ -42,7 +43,7 @@ class QPushButtonTest(UsesQApplication):
     def testBoolinSignal(self):
         b = QPushButton()
         b.setCheckable(True)
-        b.setShortcut(Qt.Key_A)
+        b.setShortcut(Qt.Key.Key_A)
         self._clicked = False
         b.toggled[bool].connect(self.buttonCb)
         b.toggle()
@@ -51,4 +52,3 @@ class QPushButtonTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index ae383f425ff1ea3f6c14704075a4dd765c0ba0ee..2a9c5c554faafe2628bc3e28f9e9aecf29264a6b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -26,4 +27,3 @@ class QSplitterTest(UsesQApplication):
 
 if __name__ == "__main__":
     unittest.main()
-
index 5a9b29dc63eb38855efa074641621042e3028c08..a6970cf4385cb27769cd29adee5f3592a2d336b7 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
-import sys
 import os
 import sys
 import unittest
@@ -13,7 +13,6 @@ init_test_paths(False)
 
 from helper.usesqapplication import UsesQApplication
 
-from PySide6.QtGui import QWindow
 from PySide6.QtWidgets import (QApplication, QFontComboBox, QLabel, QProxyStyle,
                                QStyleFactory, QWidget)
 
@@ -46,7 +45,7 @@ class SetStyleTest(UsesQApplication):
         # QFontComboBox is used because it has an QLineEdit created in C++ inside it,
         # and if the QWidget.setStyle(style) steals the ownership of the style
         # for the C++ originated widget everything will break.
-        fontComboBox = QFontComboBox(container)
+        fontComboBox = QFontComboBox(container)  # noqa: F841
         label = QLabel(container)
         label.setText('Label')
         style = QStyleFactory.create(QStyleFactory.keys()[0])
@@ -73,4 +72,3 @@ class SetStyleTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 06798b9e913be36a3d0d022c1dcd79b2f5983d28..ef9b3b30168c0e93be5c6506f3ea8cfe8c5d644a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import sys
 import os
index c43666f234e4da1eb5571b9310b0b67a0c5d59a1..bcb0b8120dfa8e46a862592105f90c0232e95d52 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -25,4 +26,3 @@ class QPenTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 8db45f24b0f5a399a54a29679f400b3ca2f4e4a8..69d308c0c4145cdbd61b61a9e3a6a5775f12b09a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 49e16e8d268f2e5098763e5601645a6113437913..5661c4e79d1e11d4270df2b526b79d602b8ee8ae 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 16929f0f2c9ebe07a83fe75f98ae3eeaae39ad44..f0fb690bb6001defa0557012870967a7e9e73250 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -47,4 +48,3 @@ class testSignalWithCPPSlot(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index b82350293286d448d0c704fcd40bd51c31af896b..9cfc526c1bb1a8b7155a2fa2c19576f58adefba7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QTextEdit and ownership problems.'''
 
index 6fa5865cf5ec00bdbc4340b81bc24787947c35ea..6a328a4cea80d77d72da37249101a79f35524e02 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QToolbar'''
 
index d069416b7e4c256436a6baadc26ab619ae6246cd..77228a928b2ff5cb46db1b4203d31025d4b72212 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index fd535bfad693b81b8e8b927d4cddb7e73a7504a2..1d259ba34f00b95f5ce6ddb10bc8f377d9c831d8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -76,7 +77,7 @@ class QWidgetTest(UsesQApplication):
 
     def testHeader(self):
         tree = QTreeView()
-        tree.setHeader(QHeaderView(Qt.Horizontal))
+        tree.setHeader(QHeaderView(Qt.Orientation.Horizontal))
         self.assertIsNotNone(tree.header())
 
 
index 6c2db32b9b73eb39670361b18aa12da90247d4f2..1ee8c45cb9f7938a52f33f0e635233b2f928c8d2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 432aba2edd89e5ad1fadffbda2a3d28deb56fba7..dd899e4551ecaf56f166d7aa077877e133a0aee1 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 Unit tests for QTreeWidgetItem
@@ -23,13 +24,12 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QTimer
 from PySide6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
 
 
 class QTreeWidgetItemTest(unittest.TestCase):
     def testClass(self):
-        app = QApplication([])
+        app = QApplication([])  # noqa: F841
         treewidget = QTreeWidget()
         item = QTreeWidgetItem(["Words and stuff"])
         item2 = QTreeWidgetItem(["More words!"])
@@ -44,4 +44,3 @@ class QTreeWidgetItemTest(unittest.TestCase):
 
 if __name__ == "__main__":
     unittest.main()
-
index fe0266309d4c012aa69627bb1ceb24044bc8188a..41a32ab761548c3bb43e1e26bdf4afb4bb924a0d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -27,7 +28,7 @@ class MyItem(QGraphicsRectItem):
         return value
 
 
-class Sequence(object):
+class Sequence:
     # Having the __getitem__ method on a class transform the Python
     # type to a PySequence.
     # Before the patch: aa75437f9119d997dd290471ac3e2cc88ca88bf1
@@ -80,9 +81,9 @@ class QVariantConversionTest(UsesQApplication):
         """
         PYSIDE-1798: Test enum is obtained correctly when return through QVariant
         """
-        self.obj.setProperty("test", Qt.SolidLine)
+        self.obj.setProperty("test", Qt.PenStyle.SolidLine)
         self.assertTrue(isinstance(self.obj.property("test"), Qt.PenStyle))
-        self.assertEqual(self.obj.property("test"), Qt.SolidLine)
+        self.assertEqual(self.obj.property("test"), Qt.PenStyle.SolidLine)
 
     def testString(self):
         self.obj.setProperty("test", "test")
index 1d9128789b04fc94c524062707333335389619ec..32da17429095dc140ace7f373f285e03c1583b81 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -11,7 +12,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication, QHBoxLayout
+from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QHBoxLayout
 from helper.usesqapplication import UsesQApplication
 
 
@@ -41,4 +42,3 @@ class QWidgetTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 2d503f0d4474de5bfc62601c6a183a274a6e0da8..28c189e1689945dc0fa47418dfae0116e49fd01c 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
-import sys
 import os
 import sys
 import unittest
index b7d3908dd52abe55b23cb9f74873f37746fda4eb..67a39e129a00c23dbd553babdb365316a01b4248 100644 (file)
@@ -1,9 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Reference count when the object is created in c++ side'''
 
-import gc
 import os
 import sys
 import unittest
@@ -14,9 +14,9 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import Qt, QPointF
+from PySide6.QtCore import QPointF
 from PySide6.QtGui import QPolygonF
-from PySide6.QtWidgets import QApplication, QGraphicsScene, QGraphicsRectItem, QGraphicsPolygonItem, QGraphicsRectItem
+from PySide6.QtWidgets import QGraphicsScene, QGraphicsPolygonItem, QGraphicsRectItem
 
 from helper.usesqapplication import UsesQApplication
 
index f156b6717b640eebb41473014f78f73c3e2547d9..1b3363ae2646d898981fd58aa4b0f29fb2a7650a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import inspect
 import os
index 564f3ff2e565699120d4e48617de042572ba6388..815e5bc498cf75772d3064e3a656f2719fddb652 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -18,10 +19,9 @@ from helper.usesqapplication import UsesQApplication
 
 class StandardPixmapTest(UsesQApplication):
     def testDefaultOptions(self):  # Bug 253
-        pixmap = self.app.style().standardPixmap(QStyle.SP_DirClosedIcon)
+        pixmap = self.app.style().standardPixmap(QStyle.StandardPixmap.SP_DirClosedIcon)
         self.assertTrue(isinstance(pixmap, QPixmap))
 
 
 if __name__ == '__main__':
     unittest.main()
-
index 92661cdff463d1064252fa56e9e89736525062fd..5993b9565533a173cd1f706b9ab7ee4683a5b009 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -24,4 +25,3 @@ class MyQWidget(QWidget):
 
 class Sentinel():
     value = 10
-
index 911fed47557ecd2ab36ab04854d167462ea9350e..91e344229141cd5f8eae4b873931a77646d5c04f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for overriding inherited protected virtual methods'''
 
@@ -81,4 +82,3 @@ if __name__ == '__main__':
     #widget.startTimer(500)
     #widget.show()
     #app.exec()
-
index b7d1e4f3fd3b11e0ec574f04da5cc271080fcea4..38187fd90c45a3d1c1af57dff65c613bc4aa7bca 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -11,7 +12,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtWidgets import QGraphicsScene, QGraphicsRectItem, QGraphicsView, QApplication
+from PySide6.QtWidgets import QGraphicsScene, QGraphicsRectItem, QGraphicsView
 from PySide6.QtGui import QBrush, QColor
 from PySide6.QtCore import QTimer
 from helper.usesqapplication import UsesQApplication
@@ -50,7 +51,7 @@ class QGraphicsItemTest(UsesQApplication):
         view = QGraphicsView(scene)
         view.setWindowTitle("virtual_pure_override_test")
 
-        rect = self.createRoundRect(scene)
+        rect = self.createRoundRect(scene)  # noqa: F841
         view.show()
         self.app.exec()
         self.assertTrue(qgraphics_item_painted)
@@ -58,4 +59,3 @@ class QGraphicsItemTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 009078ac16ffb0cd303f23413b94d52a4b867662..47ef489bb687941005c51113fff32436b5d266be 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Virtual functions with wrong return type'''
 
index cfc1c543dc1b5d637d0f7027558f4c3aa9cf714f..8fe4f6e175e5813dd9b91d708ee34a25764e02c0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -79,4 +80,3 @@ class QDomDocumentTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index e69ec1c15ca800469c0a957cfa5933fd9931c637..4636aba9991a8ed1005b2ff4e22bb98d1bba5690 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 18fc6428698b92bcfe0e5a9ef6a04ebd39fede34..72294b5f0733cb2414bbcdc652c13b44d98dbc9b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # Qt5: this is gone: from PySide6.QtGui import QMacStyle
 
@@ -12,8 +13,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtWidgets import QApplication, QLabel, QStyleFactory
-from PySide6.QtCore import QObject
+from PySide6.QtWidgets import QLabel, QStyleFactory
 
 from helper.usesqapplication import UsesQApplication
 
index 3911244ce6924bad87f58a5d3f5d020828875026..accadbc2d6914a0d0e3037ec3b8a404ef5742a6e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import sys
 
index 59f75bc46e38988815c33dac580e839c7fbb3b0e..78d79336c50e74e850c80c517319c7c26650673a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 Time a repeated Python run
diff --git a/sources/pyside6/tests/manually/sizebench.py b/sources/pyside6/tests/manually/sizebench.py
new file mode 100644 (file)
index 0000000..4ef396f
--- /dev/null
@@ -0,0 +1,151 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+"""
+sizebench.py
+
+Benchmark the size reduction of QtCore, QtGui and QtWidgets binaries.
+
+The project will be built twice in release mode (unchecked)
+ - with option "--unoptimize=all"
+ - without any option.
+
+Then values and relative improvement are printed.
+
+No argument: Use a default Python for each platform (author specific).
+
+    --python <python>   use that specific python interpreter
+    --dry-run           try it first without compilation
+    --pip               automatically install the needed modules
+"""
+import argparse
+import os
+import platform
+import re
+import subprocess
+import sys
+
+from ast import literal_eval
+from pathlib import Path
+
+defaults = {
+    "Darwin": "/Users/tismer/.pyenv/versions/3.12.5/bin/python3",
+    "Windows": "d:/py312_64/python.exe",
+    "Linux": "/home/ctismer/.pyenv/versions/3.12.5/bin/python3",
+}
+
+
+def setup_project_dir():
+    look_for = Path("testing")
+    here = Path(__file__).resolve().parent
+    while here / look_for not in here.iterdir():
+        parent = here.parent
+        if parent == here:
+            raise SystemError(look_for + " not found!")
+        here = parent
+    fsp = os.fspath(here)
+    if fsp not in sys.path:
+        sys.path.insert(0, fsp)
+
+
+def get_build_dir():
+    from testing.buildlog import builds
+    if not builds.history:
+        raise
+    return builds.history[-1].build_dir
+
+
+def check_allowed_python_versions(major, minor):
+    from build_scripts.main import config
+    pattern = r'Programming Language :: Python :: (\d+)\.(\d+)'
+    hist = []
+    for line in config.python_version_classifiers:
+        found = re.search(pattern, line)
+        if found:
+            ma = int(found.group(1))
+            mi = int(found.group(2))
+            if major == ma and minor == mi:
+                return True
+            hist.append((ma, mi))
+    raise ValueError(hist)
+
+
+def get_result_size(build_dir):
+    result_dir = Path(build_dir) / "pyside6" / "PySide6"
+    sum = 0
+    awaited = 3
+    got = 0
+    with os.scandir(result_dir) as it:
+        for entry in it:
+            name = entry.name
+            if (name.startswith(("QtCore.", "QtGui.", "QtWidgets."))
+                    and name.endswith((".so", ".pyd"))):
+                size = entry.stat().st_size
+                print(f"{name=} {size=}")
+                sum += size
+                got += 1
+    if awaited != got:
+        raise ValueError(f"got {got} values, expected {awaited}")
+    return sum
+
+
+setup_project_dir()
+plat = platform.system()
+options = [
+    "setup.py", "build", "--limited-api=no", "--skip-docs", "--no-qt-tools",
+    "--module-subset=Core,Gui,Widgets"]
+
+options_base = options + ["--unoptimize=all"]
+options_best = options
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--python", "-p", nargs="?")
+    parser.add_argument("--dry-run", "-d", action="store_true")
+    parser.add_argument("--pip", action="store_true", help="""
+        Install the necessary modules automatically, which can save some trouble""")
+    args = parser.parse_args()
+
+    python = args.python or defaults[plat] if plat in defaults else args.python
+    python = Path(python).expanduser()
+
+    if not python.exists:
+        raise ValueError(f"Python executable `{python}` not found")
+    version = subprocess.check_output([python, "-c", "import sys; print(sys.version_info[:2])"])
+    major, minor = literal_eval(version.decode())
+    try:
+        check_allowed_python_versions(major, minor)
+    except ValueError as e:
+        msg = " ".join(f"{ma}.{mi}" for (ma, mi) in e.args[0])
+        raise ValueError(f"Python versions allowed = {msg}, got {major}.{minor}") from None
+
+    needs_imports = ["packaging", "setuptools"]
+    if args.pip:
+        # This way, setuptools seems to install reliably.
+        subprocess.run([python, "-m", "pip", "install"] + needs_imports)
+        subprocess.run([python, "-m", "pip", "uninstall", "-y"] + needs_imports)
+        subprocess.run([python, "-m", "pip", "install"] + needs_imports)
+
+    skip = args.dry_run
+    cmd = [python] + options_base
+    if not skip:
+        subprocess.run(cmd)
+
+    build_dir = get_build_dir()
+    res_base = get_result_size(build_dir)
+
+    cmd = [python] + options_best
+    if not skip:
+        subprocess.run(cmd)
+
+    build_dir = get_build_dir()
+    res_best = get_result_size(build_dir)
+
+    print()
+    print(f"Compiling with {python}")
+    print(f"Platform = {plat}")
+    print(f"base size = {res_base}")
+    print(f"best size = {res_best}")
+    print(f"improvement {(res_base - res_best) / res_base:%}")
+    if skip:
+        raise ValueError("This result is only fake. Please run it again without the "
+                         "--dry-run argument")
index 38f42f3425e631f73b045d1bd0809200f5750736..8b4de5d8e91f51630be317eb77b45aec14551f11 100644 (file)
@@ -20,6 +20,7 @@ add_definitions(-DRXX_ALLOCATOR_INIT_0)
 find_package(Qt6 REQUIRED COMPONENTS Widgets)
 
 set(pysidetest_SRC
+connection.h
 containertest.cpp containertest.h
 flagstest.cpp flagstest.h
 hiddenobject.cpp hiddenobject.h
@@ -32,6 +33,7 @@ testqvariantenum.cpp testqvariantenum.h
 )
 
 set(testbinding_SRC
+${CMAKE_CURRENT_BINARY_DIR}/testbinding/connection_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/testbinding/containertest_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/testbinding/flagsnamespace_classforenum_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/testbinding/testobject_wrapper.cpp
@@ -136,6 +138,7 @@ target_link_libraries(testbinding
 add_dependencies(testbinding pyside6 QtCore QtGui QtWidgets pysidetest)
 create_generator_target(testbinding)
 
+PYSIDE_TEST(mypy_correctness_test.py)
 PYSIDE_TEST(constructor_properties_test.py)
 PYSIDE_TEST(container_test.py)
 PYSIDE_TEST(decoratedslot_test.py)
@@ -168,4 +171,4 @@ PYSIDE_TEST(signalwithdefaultvalue_test.py)
 PYSIDE_TEST(typedef_signal_test.py)
 PYSIDE_TEST(version_test.py)
 PYSIDE_TEST(mock_as_slot_test.py)
-PYSIDE_TEST(pyenum_relax_options_test.py)
+PYSIDE_TEST(pyenum_relax_options_and_lazy_star_test.py)
index 454c2b14fad8cd7bd8c0689533da04dc63aa486c..6b3ebe732a5b4cebcc8d0bec213f7a653af7c848 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -12,18 +13,26 @@ init_test_paths(False)
 
 import PySide6
 
+
 # Note:
 # "from PySide6 import *" can only be used at module level.
 # It is also really not recommended to use. But for testing,
 # the "__all__" variable is a great feature!
-
-
 class AllModulesImportTest(unittest.TestCase):
     def testAllModulesCanImport(self):
         # would also work: exec("from PySide6 import *")
         for name in PySide6.__all__:
             exec(f"import PySide6.{name}")
 
+    def testAllReappearsAfterDel(self):
+        # This is the only incompatibility that remains:
+        # After __all__ is deleted, it will re-appear.
+        PySide6.__all__ = 42
+        self.assertEqual(PySide6.__dict__["__all__"], 42)
+        del PySide6.__all__
+        self.assertTrue(PySide6.__dict__["__all__"])
+        self.assertNotEqual(PySide6.__dict__["__all__"], 42)
+
 
 if __name__ == '__main__':
     unittest.main()
index 219384e66cd9992426fdbd398c23649ed4a6ffe8..2ec2d56ee4ee387212ee928b84e9cee99a729976 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
diff --git a/sources/pyside6/tests/pysidetest/connection.h b/sources/pyside6/tests/pysidetest/connection.h
new file mode 100644 (file)
index 0000000..5217e1a
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include "pysidetest_macros.h"
+
+// PYSIDE-2792, testing converter name clashes with QMetaObject::Connection.
+class Connection
+{
+public:
+    Connection(int handle = 0) noexcept : m_handle(handle) {}
+
+    int handle() const { return m_handle; }
+
+private:
+    int m_handle;
+};
+
+#endif // CONNECTION_H
index ec6e398218c2c90e532c4feef565de9417cb4def..331955f0493777212a617a63fd19f67db17311ef 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import sys
@@ -23,22 +24,22 @@ if not is_pypy:
 class ConstructorPropertiesTest(unittest.TestCase):
 
     def setUp(self):
-        qApp or QApplication()
+        qApp or QApplication()  # noqa: F821
         if not is_pypy:
             feature.reset()
 
     def tearDown(self):
         if not is_pypy:
             feature.reset()
-        qApp.shutdown()
+        qApp.shutdown()  # noqa: F821
 
     # PYSIDE-1019: First property extension was support by the constructor.
     def testCallConstructor(self):
         label = QLabel(
-            frameStyle=QFrame.Panel | QFrame.Sunken,    # QFrame attr, no property
-            lineWidth=2,                                # QFrame property
-            text="first line\nsecond line",             # QLabel property
-            alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
+            frameStyle=QFrame.Shape.Panel | QFrame.Shadow.Sunken,  # QFrame attr, no property
+            lineWidth=2,                                           # QFrame property
+            text="first line\nsecond line",                        # QLabel property
+            alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight  # QLabel property
         )
         self.assertEqual(label.lineWidth(), 2)
         self.assertRaises(AttributeError, lambda: QLabel(
@@ -48,13 +49,13 @@ class ConstructorPropertiesTest(unittest.TestCase):
     # PYSIDE-1705: The same with snake_case
     @unittest.skipIf(is_pypy, "feature switching is not yet possible in PyPy")
     def testCallConstructor_snake(self):
-        from __feature__ import snake_case
+        from __feature__ import snake_case  # noqa: F401
 
         label = QLabel(
-            frame_style=QFrame.Panel | QFrame.Sunken,   # QFrame attr, no property
-            line_width=2,                               # QFrame property
-            text="first line\nsecond line",             # QLabel property
-            alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
+            frame_style=QFrame.Shape.Panel | QFrame.Shadow.Sunken,  # QFrame attr, no property
+            line_width=2,                                           # QFrame property
+            text="first line\nsecond line",                         # QLabel property
+            alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight  # QLabel property
         )
         self.assertEqual(label.line_width(), 2)
         self.assertRaises(AttributeError, lambda: QLabel(
@@ -64,13 +65,13 @@ class ConstructorPropertiesTest(unittest.TestCase):
     # PYSIDE-1705: The same with true_property
     @unittest.skipIf(is_pypy, "feature switching is not yet possible in PyPy")
     def testCallConstructor_prop(self):
-        from __feature__ import true_property
+        from __feature__ import true_property  # noqa: F401
 
         label = QLabel(
-            frameStyle=QFrame.Panel | QFrame.Sunken,    # QFrame attr, no property
-            lineWidth=2,                                # QFrame property
-            text="first line\nsecond line",             # QLabel property
-            alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
+            frameStyle=QFrame.Shape.Panel | QFrame.Shadow.Sunken,  # QFrame attr, no property
+            lineWidth=2,                                           # QFrame property
+            text="first line\nsecond line",                        # QLabel property
+            alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight  # QLabel property
         )
         self.assertEqual(label.lineWidth, 2)
         self.assertRaises(AttributeError, lambda: QLabel(
@@ -80,13 +81,13 @@ class ConstructorPropertiesTest(unittest.TestCase):
     # PYSIDE-1705: The same with snake_case and true_property
     @unittest.skipIf(is_pypy, "feature switching is not yet possible in PyPy")
     def testCallConstructor_prop_snake(self):
-        from __feature__ import snake_case, true_property
+        from __feature__ import snake_case, true_property  # noqa: F401
 
         label = QLabel(
-            frame_style=QFrame.Panel | QFrame.Sunken,   # QFrame attr, no property
+            frame_style=QFrame.Shape.Panel | QFrame.Shadow.Sunken,   # QFrame attr, no property
             line_width=2,                               # QFrame property
             text="first line\nsecond line",             # QLabel property
-            alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
+            alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight  # QLabel property
         )
         self.assertEqual(label.line_width, 2)
         self.assertRaises(AttributeError, lambda: QLabel(
index c83e1f26c7430be0230bf6b2d269531370c43f5f..04eed83bc0315a423e8b4505fee586c86d4449fa 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 3ec9ac9e3422f33bb473b23d043d71a6f30211b1..f400324b589a1fbba1f0abe0724ef17e9d60fe09 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -47,4 +48,3 @@ class DecoratedSlotTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 8964ec1eda44f1392dba81579ae2ae7de8fbc12c..082885acbf23086be676a867667ccfa57671a255 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,7 +18,7 @@ from PySide6.QtCore import Qt
 from PySide6.QtGui import QStandardItem, QStandardItemModel
 from PySide6.QtWidgets import (QAbstractItemDelegate, QComboBox,
                                QSpinBox, QStyledItemDelegate,
-                               QStyleOptionViewItem, QWidget)
+                               QStyleOptionViewItem)
 
 id_text = 'This is me'
 
@@ -49,7 +50,7 @@ class EditorCreatedByDelegateTest(UsesQApplication):
         editor = view.getEditorWidgetFromItemDelegate()
         self.assertEqual(type(editor), QComboBox)
         self.assertEqual(editor.count(), 1)
-        self.assertEqual(editor.itemData(0, Qt.DisplayRole), id_text)
+        self.assertEqual(editor.itemData(0, Qt.ItemDataRole.DisplayRole), id_text)
         editor.metaObject()
 
     def testDelegateKeepsReferenceToEditor(self):
@@ -59,7 +60,7 @@ class EditorCreatedByDelegateTest(UsesQApplication):
         editor = view.getEditorWidgetFromItemDelegate()
         self.assertEqual(type(editor), QComboBox)
         self.assertEqual(editor.count(), 1)
-        self.assertEqual(editor.itemData(0, Qt.DisplayRole), id_text)
+        self.assertEqual(editor.itemData(0, Qt.ItemDataRole.DisplayRole), id_text)
         editor.metaObject()
 
     def testIntDelegate(self):
@@ -67,7 +68,7 @@ class EditorCreatedByDelegateTest(UsesQApplication):
            for anything that fits into a int. Verify by checking that a spin
            box is created as item view editor for int."""
         item = QStandardItem()
-        item.setData(123123, Qt.EditRole)  # <-- QVariant conversion here
+        item.setData(123123, Qt.ItemDataRole.EditRole)  # <-- QVariant conversion here
         model = QStandardItemModel()
         model.appendRow(item)
         style_option = QStyleOptionViewItem()
@@ -78,4 +79,3 @@ class EditorCreatedByDelegateTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index 83283453043661810af1143c2843e017022799a4..cfbe3ac724f8ea4a3fed1494e65894c2ecc065a3 100644 (file)
@@ -1,5 +1,6 @@
-# Copyright (C) 2022 The Qt Company Ltd.
+# Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -25,8 +26,8 @@ class ListConnectionTest(unittest.TestCase):
         self.assertEqual(TestObjectWithoutNamespace.Enum2.Option4, 4)
 
     def testFlagComparisonOperators(self):  # PYSIDE-1696, compare to self
-        f1 = Qt.AlignHCenter | Qt.AlignBottom
-        f2 = Qt.AlignHCenter | Qt.AlignBottom
+        f1 = Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom
+        f2 = Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom
         self.assertTrue(f1 == f1)
         self.assertTrue(f1 <= f1)
         self.assertTrue(f1 >= f1)
@@ -41,15 +42,17 @@ class ListConnectionTest(unittest.TestCase):
         self.assertFalse(f1 < f2)
         self.assertFalse(f1 > f2)
 
-        self.assertTrue(Qt.AlignHCenter < Qt.AlignBottom)
-        self.assertFalse(Qt.AlignHCenter > Qt.AlignBottom)
-        self.assertFalse(Qt.AlignBottom < Qt.AlignHCenter)
-        self.assertTrue(Qt.AlignBottom > Qt.AlignHCenter)
+        self.assertTrue(Qt.AlignmentFlag.AlignHCenter < Qt.AlignmentFlag.AlignBottom)
+        self.assertFalse(Qt.AlignmentFlag.AlignHCenter > Qt.AlignmentFlag.AlignBottom)
+        self.assertFalse(Qt.AlignmentFlag.AlignBottom < Qt.AlignmentFlag.AlignHCenter)
+        self.assertTrue(Qt.AlignmentFlag.AlignBottom > Qt.AlignmentFlag.AlignHCenter)
 
 
 # PYSIDE-1735: We are testing that opcodes do what they are supposed to do.
 #              This is needed in the PyEnum forgiveness mode where we need
 #              to introspect the code if an Enum was called with no args.
+
+# flake8: noqa
 class InvestigateOpcodesTest(unittest.TestCase):
 
     def probe_function1(self):
@@ -89,15 +92,6 @@ class InvestigateOpcodesTest(unittest.TestCase):
                     ('LOAD_CONST',  100, 0),
                     ('RETURN_VALUE', 83, None)]
 
-        if sys.version_info[:2] <= (3, 6):
-
-            result_2 = [('LOAD_GLOBAL',   116, 0),
-                        ('LOAD_ATTR',     106, 1),
-                        ('CALL_FUNCTION', 131, 0),
-                        ('STORE_FAST',    125, 1),
-                        ('LOAD_CONST',    100, 0),
-                        ('RETURN_VALUE',   83, None)]
-
         if sys.version_info[:2] == (3, 11):
             # Note: Python 3.11 is a bit more complex because it can optimize itself.
             # Opcodes are a bit different, and a hidden second code object is used.
@@ -162,25 +156,40 @@ class InvestigateOpcodesTest(unittest.TestCase):
             self.assertEqual(self.read_code(self.probe_function2, adaptive=True), result_3)
             self.assertEqual(self.get_sizes(self.probe_function2, adaptive=True), sizes_3)
 
-        if sys.version_info[:2] >= (3, 12):
+        if sys.version_info[:2] == (3, 12):
 
-            result_1 = [('RESUME', 151, 0),
-                        ('LOAD_GLOBAL', 116, 0),
-                        ('LOAD_ATTR', 106, 2),
-                        ('STORE_FAST', 125, 1),
+            result_1 = [('RESUME',       151, 0),
+                        ('LOAD_GLOBAL',  116, 0),
+                        ('LOAD_ATTR',    106, 2),
+                        ('STORE_FAST',   125, 1),
                         ('RETURN_CONST', 121, 0)]
 
-            result_2 = [('RESUME', 151, 0),
-                        ('LOAD_GLOBAL', 116, 1),
-                        ('LOAD_ATTR', 106, 2),
-                        ('CALL', 171, 0),
-                        ('STORE_FAST', 125, 1),
+            result_2 = [('RESUME',       151, 0),
+                        ('LOAD_GLOBAL',  116, 1),
+                        ('LOAD_ATTR',    106, 2),
+                        ('CALL',         171, 0),
+                        ('STORE_FAST',   125, 1),
                         ('RETURN_CONST', 121, 0)]
 
+        if sys.version_info[:2] >= (3, 13):
+
+            result_1 = [('RESUME',       149, 0),
+                        ('LOAD_GLOBAL',   91, 0),
+                        ('LOAD_ATTR',     82, 2),
+                        ('STORE_FAST',   110, 1),
+                        ('RETURN_CONST', 103, 0)]
+
+            result_2 = [('RESUME',       149, 0),
+                        ('LOAD_GLOBAL',   91, 0),
+                        ('LOAD_ATTR',     82, 2),
+                        ('PUSH_NULL',     34, None),
+                        ('CALL',          53, 0),
+                        ('STORE_FAST',   110, 1),
+                        ('RETURN_CONST', 103, 0)]
+
         self.assertEqual(self.read_code(self.probe_function1), result_1)
         self.assertEqual(self.read_code(self.probe_function2), result_2)
 
 
 if __name__ == '__main__':
     unittest.main()
-
index b58232a1b820525c58cc8d93e9ccc3c92ee8359f..19eee7506463e9ca08ea44f1322ffebe1f708d46 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -106,4 +107,3 @@ class HomonymousMultipleInheritanceTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index bdb2ae7be14af36e320cd81b3e8c8d95d070886c..1a54f1848e5cf531022e92fd815a9b28de3c32d4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 iterable_test.py
index da4bc298d88a6be6e5e99d773d3984799c76c653..6fd1f919431ea04b7bc441f9e028c711df74e162 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -31,4 +32,3 @@ class ListConnectionTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 1d536c0ae7ed53de343a6dd1bf275be373642ad2..7e1ddbb17aa1ee7800779477118c6e6954ec2692 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' PYSIDE-315: https://bugreports.qt.io/browse/PYSIDE-315
     Test that all signals and slots of a class (including any mixin classes)
@@ -19,7 +20,7 @@ init_test_paths(False)
 from PySide6.QtCore import QObject, Signal, Slot
 
 
-class Mixin(object):
+class Mixin:
     mixinSignal = Signal()
 
     def __init__(self, *args, **kwargs):
@@ -38,7 +39,7 @@ class MixinTwo(Mixin):
         self.mixinTwoSlotCalled = True
 
 
-class MixinThree(object):
+class MixinThree:
     mixinThreeSignal = Signal()
 
     def __init__(self, *args, **kwargs):
@@ -202,4 +203,3 @@ class MixinTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 39f52adea1076e587d439935b9ed482d701c5a31..f6504a78929dfc547e8eac152b93104aa909840f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """ PYSIDE-1755: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1755
     Tests that a unittest.mock.MagicMock() can be used as a slot for quick
index 53231aebe06e6bcb4dfe264dc10ade0e4d37534a..f40f96d351e3ddaa89812ca6f34b7e220de68fa7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -82,4 +83,3 @@ class ModelViewTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 49550ba55f8137c5b55ab1fe9089079a2ac8cc90..fee2eea8d1689a3b99f529b8bbdceb4e8470a229 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -21,7 +22,7 @@ def xprint(*args, **kw):
 
 
 # This is the original testcase of PYSIDE-1564
-class Age(object):
+class Age:
     def __init__(self, age=0, **kwds):
         super().__init__(**kwds)
 
@@ -116,7 +117,7 @@ class II(G, H, QtWidgets.QLabel):
 
 # PYSIDE-2294: Friedemann's test adapted.
 #              We need to ignore positional args in mixin classes.
-class Ui_X_MainWindow(object):  # Emulating uic
+class Ui_X_MainWindow:  # Emulating uic
     def setupUi(self, MainWindow):
         MainWindow.resize(400, 300)
         self.lbl = QLabel(self)
@@ -159,7 +160,7 @@ class AdditionalMultipleInheritanceTest(UsesQApplication):
 
 # PYSIDE-2654: Additional missing init test.
 #              This must work if no __init__ is defined (Ui_Form)
-class Ui_Form(object):
+class Ui_Form:
     pass
 
 
diff --git a/sources/pyside6/tests/pysidetest/mypy_correctness_test.py b/sources/pyside6/tests/pysidetest/mypy_correctness_test.py
new file mode 100644 (file)
index 0000000..fd25309
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import os
+import sys
+import unittest
+import subprocess
+import time
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+try:
+    import mypy     # noqa: F401
+    HAVE_MYPY = True
+except ModuleNotFoundError:
+    HAVE_MYPY = False
+
+import PySide6
+from PySide6 import SKIP_MYPY_TEST
+
+qtest_env = os.environ.get("QTEST_ENVIRONMENT", "")
+is_ci = qtest_env == "ci"
+# When we are in COIN, we enforce mypy existence, to prevent misconfigurations.
+USE_MYPY = True if is_ci else HAVE_MYPY
+
+
+@unittest.skipIf(not USE_MYPY, "The mypy test was skipped because mypy is not installed")
+@unittest.skipIf(SKIP_MYPY_TEST, "The mypy test was disabled")
+class MypyCorrectnessTest(unittest.TestCase):
+
+    def setUp(self):
+        self.pyside_dir = Path(PySide6.__file__).parent
+        self.build_dir = self.pyside_dir.parent.parent
+        os.chdir(self.build_dir)
+        self.project_dir = Path(__file__).resolve().parents[4]
+        # For safety about future changes, check that we are sitting above the sources dir.
+        self.assertTrue((self.project_dir / "sources").exists())
+        # Check if the project dir can be written. If so, put the mypy cache there.
+        test_fname = self.project_dir / ".tmp test writable"
+        try:
+            with test_fname.open("w") as f:
+                f.write("works!")
+                f.close()
+                test_fname.unlink()
+            self.cache_dir = self.project_dir / f".pyside{PySide6.__version__}_mypy_cache"
+        except Exception:
+            self.cache_dir = ".mypy_cache"  # This is the mypy default.
+
+    def testMypy(self):
+        self.assertTrue(HAVE_MYPY)
+        insert_version = ["--python-version", "3.11"] if sys.version_info[:2] < (3, 11) else []
+        cmd = ([sys.executable, "-m", "mypy", "--cache-dir", self.cache_dir]
+               + insert_version + [self.pyside_dir])
+        time_pre = time.time()
+        ret = subprocess.run(cmd, capture_output=True)
+        time_post = time.time()
+        for line in ret.stdout.decode("utf-8").split("\n"):
+            print(line)
+        print(f"Time used for mypy test = {(time_post - time_pre):.5} s")
+        self.assertEqual(ret.returncode, 0)
+
+
+if __name__ == '__main__':
+    unittest.main()
index 924a2eea778d16e1524a9ed7e4018f924ef646c3..722adc5cb6458ede4b54a06c897068c417def88e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,14 +11,18 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6 import *
+from PySide6 import *  # noqa: F401,F403
 from PySide6.support.signature import get_signature
 for modname, mod in sys.modules.items():
-    # Python 2 leaves "None" in the dict.
-    if modname.startswith("PySide6.") and mod is not None:
+    if modname.startswith("PySide6."):
         print("importing", modname)
         exec("import " + modname)
 
+# To help linters
+import PySide6.QtCore
+import PySide6.QtGui
+import PySide6.QtWidgets
+
 # This test tests the existence and callability of the newly existing functions,
 # after the inheritance was made complete in the course of PYSIDE-331.
 
@@ -26,13 +31,14 @@ new_functions = """
     PySide6.QtCore.QAbstractListModel().parent()
     PySide6.QtCore.QAbstractTableModel().parent()
     PySide6.QtCore.QFile().resize(qint64)
-    m = PySide6.QtCore.QMutex(); m.tryLock(); m.unlock() # prevent a message "QMutex: destroying locked mutex"
+    # prevent a message "QMutex: destroying locked mutex"
+    m = PySide6.QtCore.QMutex(); m.tryLock(); m.unlock()
     PySide6.QtCore.QSortFilterProxyModel().parent()
     PySide6.QtCore.QTemporaryFile(tfarg).open(openMode)
 """
 
 new_functions += """
-    PySide6.QtGui.QStandardItemModel().insertColumn(int,qModelIndex)
+    PySide6.QtGui.QStandardItemModel().insertColumn(_int,qModelIndex)
     PySide6.QtGui.QStandardItemModel().parent()
     # PySide6.QtGui.QTextList(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11
     # PySide6.QtGui.QTextTable(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11
@@ -46,14 +52,15 @@ new_functions += """
     PySide6.QtWidgets.QBoxLayout(direction).addWidget(qWidget)
     PySide6.QtWidgets.QColorDialog().open()
     PySide6.QtWidgets.QFileDialog().open()
-    PySide6.QtWidgets.QFileSystemModel().index(int,int,qModelIndex)
+    PySide6.QtWidgets.QFileSystemModel().index(_int,_int,qModelIndex)
     PySide6.QtWidgets.QFileSystemModel().parent()
     PySide6.QtWidgets.QFontDialog().open()
     PySide6.QtWidgets.QGestureEvent([]).accept()
     PySide6.QtWidgets.QGestureEvent([]).ignore()
     PySide6.QtWidgets.QGestureEvent([]).isAccepted()
-    PySide6.QtWidgets.QGestureEvent([]).setAccepted(bool)
-    # PySide6.QtWidgets.QGraphicsView().render(qPaintDevice,qPoint,qRegion,renderFlags) # QPaintDevice: NotImplementedError
+    PySide6.QtWidgets.QGestureEvent([]).setAccepted(_bool)
+    # QPaintDevice: NotImplementedError
+    # PySide6.QtWidgets.QGraphicsView().render(qPaintDevice,qPoint,qRegion,renderFlags)
     PySide6.QtWidgets.QGridLayout().addWidget(qWidget)
     PySide6.QtWidgets.QInputDialog().open()
     PySide6.QtWidgets.QLineEdit().addAction(qAction)
@@ -70,13 +77,16 @@ new_functions += """
     # PySide6.QtPrintSupport.QPageSetupDialog().open() # Segmentation fault: 11
     # PySide6.QtPrintSupport.QPrintDialog().open() # opens the dialog, but works
     PySide6.QtPrintSupport.QPrintDialog().printer()
-    PySide6.QtPrintSupport.QPrintPreviewDialog().open() # note: this prints something, but really shouldn't ;-)
+    # note: this prints something, but really shouldn't ;-)
+    PySide6.QtPrintSupport.QPrintPreviewDialog().open()
 """ if "PySide6.QtPrintSupport" in sys.modules else ""
 
 new_functions += """
     PySide6.QtHelp.QHelpContentModel().parent()
-    # PySide6.QtHelp.QHelpIndexModel().createIndex(int,int,quintptr) # returned NULL without setting an error
-    # PySide6.QtHelp.QHelpIndexModel().createIndex(int,int,object()) # returned NULL without setting an error
+    # returns NULL without setting an error
+    # PySide6.QtHelp.QHelpIndexModel().createIndex(_int,_int,quintptr)
+    # returns NULL without setting an error
+    # PySide6.QtHelp.QHelpIndexModel().createIndex(_int,_int,object())
 """ if "PySide6.QtHelp" in sys.modules else ""
 
 new_functions += """
@@ -96,34 +106,34 @@ class MainTest(unittest.TestCase):
                 break
             except AttributeError:
                 continue
-        bool = True
-        int = 42
-        qint64 = 42
-        tfarg = os.path.join(PySide6.QtCore.QDir.tempPath(), "XXXXXX.tmp")
-        findStr = 'bla'
-        orientation = PySide6.QtCore.Qt.Orientations()
-        openMode = PySide6.QtCore.QIODevice.OpenMode(PySide6.QtCore.QIODevice.ReadOnly)
-        qModelIndex = PySide6.QtCore.QModelIndex()
-        transformationMode = PySide6.QtCore.Qt.TransformationMode()
-        qObject = PySide6.QtCore.QObject()
-        qPoint = PySide6.QtCore.QPoint()
+        _bool = True  # noqa: F841,F405
+        _int = 42  # noqa: F841,F405
+        qint64 = 42  # noqa: F841,F405
+        tfarg = os.path.join(PySide6.QtCore.QDir.tempPath(), "XXXXXX.tmp")  # noqa: F841,F405
+        findStr = 'bla'  # noqa: F841,F405
+        orientation = PySide6.QtCore.Qt.Orientations()  # noqa: F841,F405
+        openMode = PySide6.QtCore.QIODevice.OpenMode(PySide6.QtCore.QIODevice.OpenModeFlag.ReadOnly)  # noqa: F841,F405,E501
+        qModelIndex = PySide6.QtCore.QModelIndex()  # noqa: F841,F405
+        transformationMode = PySide6.QtCore.Qt.TransformationMode()  # noqa: F841,F405
+        qObject = PySide6.QtCore.QObject()  # noqa: F841,F405
+        qPoint = PySide6.QtCore.QPoint()  # noqa: F841,F405
         try:
-            PySide6.QtGui
+            PySide6.QtGui  # noqa: F405
             #qPaintDevice = PySide6.QtGui.QPaintDevice()  # NotImplementedError
-            qTextDocument = PySide6.QtGui.QTextDocument()
-            qTextFormat = PySide6.QtGui.QTextFormat()
-            quintptr = 42
-            qFont = PySide6.QtGui.QFont()
-            qPalette = PySide6.QtGui.QPalette()
+            qTextDocument = PySide6.QtGui.QTextDocument()  # noqa: F841,F405
+            qTextFormat = PySide6.QtGui.QTextFormat()  # noqa: F841,F405
+            quintptr = 42  # noqa: F841,F405
+            qFont = PySide6.QtGui.QFont()  # noqa: F841,F405
+            qPalette = PySide6.QtGui.QPalette()  # noqa: F841,F405
         except AttributeError:
             pass
         try:
-            PySide6.QtWidgets
-            direction = PySide6.QtWidgets.QBoxLayout.Direction()
-            qWidget = PySide6.QtWidgets.QWidget()
-            qStyleOptionFrame = PySide6.QtWidgets.QStyleOptionFrame()
-            qAction = PySide6.QtGui.QAction(qObject)
-            renderFlags = PySide6.QtWidgets.QWidget.RenderFlags
+            PySide6.QtWidgets  # noqa: F405
+            direction = PySide6.QtWidgets.QBoxLayout.Direction()  # noqa: F841,F405
+            qWidget = PySide6.QtWidgets.QWidget()  # noqa: F841,F405
+            qStyleOptionFrame = PySide6.QtWidgets.QStyleOptionFrame()  # noqa: F841,F405
+            qAction = PySide6.QtGui.QAction(qObject)  # noqa: F841,F405
+            renderFlags = PySide6.QtWidgets.QWidget.RenderFlags  # noqa: F841,F405
         except AttributeError:
             pass
 
@@ -146,12 +156,13 @@ class MainTest(unittest.TestCase):
         palette() without argument.
         """
         try:
-            qApp = (PySide6.QtWidgets.QApplication.instance() or
-                    PySide6.QtWidgets.QApplication([]))
+            qApp = (  # noqa: F841
+                PySide6.QtWidgets.QApplication.instance() or PySide6.QtWidgets.QApplication([])  # noqa: F405,E501
+            )
         except AttributeError:
             unittest.TestCase().skipTest("this test makes only sense if QtWidgets is available.")
 
-        sigs = get_signature(PySide6.QtWidgets.QApplication.palette)
+        sigs = get_signature(PySide6.QtWidgets.QApplication.palette)  # noqa: F405
         self.assertEqual(len(sigs), 3)
 
 
index 0c4f39f6631ad2bb01f7093fc47479206b8007a5..49ba6969f09ba3db25c57d37a1b70ba71ce665d9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 8eb7812d1ceb44719493a33f3732119880557a44..00d7aad1b2efa760412203e243f394f3759ce4d8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 1209aad4fbcce7fc01b8376f24ade37808b3a787..faf509f1fdb0feba96150ab6e1de43a85f366dc5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 Test for PySide's Property
@@ -26,9 +27,6 @@ init_test_paths(False)
 
 from PySide6.QtCore import Property, QObject
 
-# This are the original imports.
-import sys
-import unittest
 has_test = False
 try:
     from test import support
@@ -91,7 +89,7 @@ class SubClass(BaseClass):
         raise PropertyDel(self._spam)
 
 
-class PropertyDocBase(object):
+class PropertyDocBase:
     _spam = 1
 
     def _get_spam(self):
@@ -190,7 +188,7 @@ class PropertyTests(unittest.TestCase):
     @unittest.skipIf(sys.flags.optimize >= 2,
                      "Docstrings are omitted with -O2 and above")
     def test_property_decorator_doc_writable(self):
-        class PropertyWritableDoc(object):
+        class PropertyWritableDoc:
 
             @Property(object)
             def spam(self):
@@ -202,7 +200,7 @@ class PropertyTests(unittest.TestCase):
         sub.__class__.spam.__doc__ = 'Spam'
         self.assertEqual(sub.__class__.spam.__doc__, 'Spam')
 
-    if has_test:  # This test has no support in Python 2
+    if has_test:
         @support.refcount_test
         def test_refleaks_in___init__(self):
             gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
diff --git a/sources/pyside6/tests/pysidetest/pyenum_relax_options_and_lazy_star_test.py b/sources/pyside6/tests/pysidetest/pyenum_relax_options_and_lazy_star_test.py
new file mode 100644 (file)
index 0000000..8eadbdb
--- /dev/null
@@ -0,0 +1,143 @@
+#!/usr/bin/python
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+"""
+PYSIDE-1735: Testing different relax options for Enums
+
+This test uses different configurations and initializes QtCore with it.
+Because re-initialization is not possible, the test uses a subprocess
+for it. This makes the test pretty slow.
+
+Maybe we should implement a way to re-initialize QtCore enums without
+using subprocess, just to speed this up??
+"""
+
+import os
+import sys
+import unittest
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+import subprocess
+import tempfile
+from textwrap import dedent
+
+
+def runtest(program):
+    passed_path = os.fspath(Path(__file__).resolve().parents[1])
+    with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".py") as fp:
+        preamble = dedent(f"""
+            import os
+            import sys
+            from pathlib import Path
+            sys.path.append({passed_path!r})
+            from init_paths import init_test_paths
+            init_test_paths(False)
+            """)
+        print(preamble, program, file=fp)
+        fp.close()
+        try:
+            ret = subprocess.run([sys.executable, fp.name], check=False, capture_output=True)
+        finally:
+            os.unlink(fp.name)
+        for line in ret.stdout.decode("utf-8").split("\n"):
+            print(line)
+        return ret.returncode == 0
+
+
+def testprog2(option):
+    return runtest(dedent(f"""
+        sys.pyside6_option_python_enum = {option}
+        from PySide6 import QtCore
+        from enum import IntEnum
+        assert(issubclass(QtCore.Qt.DateFormat, IntEnum))
+        """))
+
+
+def testprog4(option):
+    return runtest(dedent(f"""
+        sys.pyside6_option_python_enum = {option}
+        from PySide6 import QtCore
+        QtCore.QtDebugMsg
+        """))
+
+
+def testprog8_16(option):
+    # this test needs flag 16, or the effect would be hidden by forgiving mode
+    return runtest(dedent(f"""
+        sys.pyside6_option_python_enum = {option}
+        from PySide6 import QtCore
+        QtCore.Qt.AlignTop
+        """))
+
+
+def testprog32(option):
+    return runtest(dedent(f"""
+        sys.pyside6_option_python_enum = {option}
+        from PySide6 import QtCore
+        QtCore.Qt.Alignment
+        """))
+
+
+def testprog64(option):
+    return runtest(dedent(f"""
+        sys.pyside6_option_python_enum = {option}
+        from PySide6 import QtCore
+        QtCore.Qt.AlignmentFlag()
+        """))
+
+
+def testprog128_lazy_star(option):
+    return runtest(dedent(f"""
+        sys.pyside6_option_python_enum = {option}
+        from PySide6 import QtCore
+
+        # triggers a lazy star import error:
+        from PySide6.QtCore import *
+        # triggers a NameError
+        QDirListing.DirEntry
+        # End of lazy star
+
+        QtCore.Qt.Key(1234567)
+        """))
+
+
+class TestPyEnumRelaxOption(unittest.TestCase):
+    """
+    This test is a bit involved, because we cannot unload QtCore after it is loaded once.
+    We use subprocess to test different cases, anyway.
+    """
+
+    def test_enumIsIntEnum(self):
+        self.assertTrue(testprog2(2))
+        self.assertFalse(testprog2(4))
+
+    def test_globalDefault(self):
+        self.assertTrue(testprog4(4))
+        self.assertFalse(testprog4(1))
+        self.assertTrue(testprog4(12))
+
+    def test_localDefault(self):
+        self.assertTrue(testprog8_16(8 + 16))
+        self.assertFalse(testprog8_16(0 + 16))
+
+    def test_fakeRenames(self):
+        self.assertTrue(testprog32(1))
+        self.assertFalse(testprog32(32))
+
+    def test_zeroDefault(self):
+        self.assertTrue(testprog64(1))
+        self.assertFalse(testprog64(64))
+
+    def test_Missing(self):
+        self.assertTrue(testprog128_lazy_star(1))
+        self.assertFalse(testprog128_lazy_star(128))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/sources/pyside6/tests/pysidetest/pyenum_relax_options_test.py b/sources/pyside6/tests/pysidetest/pyenum_relax_options_test.py
deleted file mode 100644 (file)
index 625f9cd..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/python
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-"""
-PYSIDE-1735: Testing different relax options for Enums
-
-This test uses different configurations and initializes QtCore with it.
-Because re-initialization is not possible, the test uses a subprocess
-for it. This makes the test pretty slow.
-
-Maybe we should implement a way to re-initialize QtCore enums without
-using subprocess, just to speed this up??
-"""
-
-import os
-import sys
-import unittest
-
-from pathlib import Path
-sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
-init_test_paths(False)
-
-import subprocess
-import tempfile
-from textwrap import dedent
-
-
-def runtest(program):
-    passed_path = os.fspath(Path(__file__).resolve().parents[1])
-    with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".py") as fp:
-        preamble = dedent(f"""
-            import os
-            import sys
-            from pathlib import Path
-            sys.path.append({passed_path!r})
-            from init_paths import init_test_paths
-            init_test_paths(False)
-            """)
-        print(preamble, program, file=fp)
-        fp.close()
-        try:
-            subprocess.run([sys.executable, fp.name], check=True, capture_output=True)
-            return True
-        except subprocess.CalledProcessError as e:
-            print(f"\ninfo: {e.__class__.__name__}: {e.stderr}")
-            return False
-        finally:
-            os.unlink(fp.name)
-
-
-def testprog2(option):
-    return runtest(dedent(f"""
-        sys.pyside6_option_python_enum = {option}
-        from PySide6 import QtCore
-        from enum import IntEnum
-        assert(issubclass(QtCore.Qt.DateFormat, IntEnum))
-        """))
-
-
-def testprog4(option):
-    return runtest(dedent(f"""
-        sys.pyside6_option_python_enum = {option}
-        from PySide6 import QtCore
-        QtCore.QtDebugMsg
-        """))
-
-
-def testprog8_16(option):
-    # this test needs flag 16, or the effect would be hidden by forgiving mode
-    return runtest(dedent(f"""
-        sys.pyside6_option_python_enum = {option}
-        from PySide6 import QtCore
-        QtCore.Qt.AlignTop
-        """))
-
-
-def testprog32(option):
-    return runtest(dedent(f"""
-        sys.pyside6_option_python_enum = {option}
-        from PySide6 import QtCore
-        QtCore.Qt.Alignment
-        """))
-
-
-def testprog64(option):
-    return runtest(dedent(f"""
-        sys.pyside6_option_python_enum = {option}
-        from PySide6 import QtCore
-        QtCore.Qt.AlignmentFlag()
-        """))
-
-
-def testprog128(option):
-    return runtest(dedent(f"""
-        sys.pyside6_option_python_enum = {option}
-        from PySide6 import QtCore
-        QtCore.Qt.Key(1234567)
-        """))
-
-
-class TestPyEnumRelaxOption(unittest.TestCase):
-    """
-    This test is a bit involved, because we cannot unload QtCore after it is loaded once.
-    We use subprocess to test different cases, anyway.
-    """
-
-    def test_enumIsIntEnum(self):
-        self.assertTrue(testprog2(2))
-        self.assertFalse(testprog2(4))
-
-    def test_globalDefault(self):
-        self.assertTrue(testprog4(4))
-        self.assertFalse(testprog4(1))
-        self.assertTrue(testprog4(12))
-
-    def test_localDefault(self):
-        self.assertTrue(testprog8_16(8 + 16))
-        self.assertFalse(testprog8_16(0 + 16))
-
-    def test_fakeRenames(self):
-        self.assertTrue(testprog32(1))
-        self.assertFalse(testprog32(32))
-
-    def test_zeroDefault(self):
-        self.assertTrue(testprog64(1))
-        self.assertFalse(testprog64(64))
-
-    def test_Missing(self):
-        self.assertTrue(testprog128(1))
-        self.assertFalse(testprog128(128))
-
-
-if __name__ == "__main__":
-    unittest.main()
index 6f784dc583586387a462dc64761659a65a74ab23..146bc2c6141f798c3be07ff3d2adf10d496ed13b 100644 (file)
@@ -5,6 +5,7 @@
 #define PYSIDETEST_GLOBAL_H
 
 // PySide global.h file
+#include "connection.h"
 #include "containertest.h"
 #include "testobject.h"
 #include "testview.h"
index 2a3f340142b81556cb9c91d4839f6fc5e7014638..0d1ae3754a3bd19664940e6503fbdca5d52e5be5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-import PySide6
+import PySide6  # noqa
 
 # This test tests the new "macro" feature of qApp.
 # It also uses the qApp variable to finish the instance and start over.
@@ -29,9 +30,9 @@ class qAppMacroTest(unittest.TestCase):
         except ImportError:
             QtWidgets = QtGui = QtCore
         # qApp is in the builtins
-        self.assertEqual(bool(qApp), False)
+        self.assertEqual(bool(qApp), False)  # noqa
         # and the type is None
-        self.assertTrue(qApp is None)
+        self.assertTrue(qApp is None)  # noqa
         # now we create an application for all cases
         classes = (QtCore.QCoreApplication,
                    QtGui.QGuiApplication,
@@ -40,14 +41,14 @@ class qAppMacroTest(unittest.TestCase):
         for klass in classes:
             print("CREATED", klass([]), file=fil)
             fil.flush()
-            qApp.shutdown()
-            print("DELETED qApp", qApp, file=fil)
+            qApp.shutdown()  # noqa
+            print("DELETED qApp", qApp, file=fil)  # noqa
             fil.flush()
         # creating without deletion raises:
         QtCore.QCoreApplication([])
         with self.assertRaises(RuntimeError):
             QtCore.QCoreApplication([])
-        self.assertEqual(QtCore.QCoreApplication.instance(), qApp)
+        self.assertEqual(QtCore.QCoreApplication.instance(), qApp)  # noqa
 
     def test_1093(self):
         # Test that without creating a QApplication staticMetaObject still exists.
@@ -61,7 +62,7 @@ class qAppMacroTest(unittest.TestCase):
         if app is None:
             app = QtCore.QCoreApplication([])
         self.assertTrue(QtCore.QObject.staticMetaObject is not None)
-        qApp.shutdown()
+        qApp.shutdown()  # noqa
 
 
 if __name__ == '__main__':
index faefc8169786afe2fcc811268786e4f11bdfb4ac..83b25b9789f92e569ce66df1f600e998befd7958 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import enum
 import os
@@ -34,15 +35,16 @@ class QVariantTest(UsesQApplication):
 
     def testQKeySequenceQVariantOperator(self):
         # bug #775
-        ks = QKeySequence(Qt.ShiftModifier, Qt.ControlModifier, Qt.Key_P, Qt.Key_R)
+        ks = QKeySequence(Qt.KeyboardModifier.ShiftModifier, Qt.KeyboardModifier.ControlModifier,
+                          Qt.Key.Key_P, Qt.Key.Key_R)
         self.assertEqual(TestObject.checkType(ks), 4107)
 
     def testQKeySequenceMoreVariations(self):
-        QAction().setShortcut(Qt.CTRL | Qt.Key_B)
-        QAction().setShortcut(Qt.CTRL | Qt.ALT | Qt.Key_B)
-        QAction().setShortcut(Qt.CTRL | Qt.AltModifier | Qt.Key_B)
-        QAction().setShortcut(QKeySequence(QKeyCombination(Qt.CTRL | Qt.Key_B)))
-        QKeySequence(Qt.CTRL | Qt.Key_Q)
+        QAction().setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_B)
+        QAction().setShortcut(Qt.Modifier.CTRL | Qt.Modifier.ALT | Qt.Key.Key_B)
+        QAction().setShortcut(Qt.Modifier.CTRL | Qt.KeyboardModifier.AltModifier | Qt.Key.Key_B)
+        QAction().setShortcut(QKeySequence(QKeyCombination(Qt.Modifier.CTRL | Qt.Key.Key_B)))
+        QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q)
 
     def testEnum(self):
         # Testing C++ class
index 863f17657d335e10c4d690872933e277a188348f..28fc701b08a687077e8b566e52ddc24e0726d840 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # Copyright (C) 2019 Andreas Beckermann
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import sys
@@ -61,4 +62,3 @@ class QObjectDerivedReprTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 6f49d69b1cce843bd5664d31947a145573c18129..6c436472e5d75b70692978b410515ecc09971f44 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
-import gc
 import os
 import sys
 import unittest
index b94281643ef13f4e586affeb6166e0d9c7e6cfa4..06ee338fd34dfc0204f3c80443588e0ea1d621a5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' PYSIDE-315: https://bugreports.qt.io/browse/PYSIDE-315
     Test that creating a signal in the wrong order triggers a warning. '''
index 6025d119d268afef8e994ae5dffc9949e9a59fda..2253572b042f1ec96edff859e375f15d57f5e53a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 PYSIDE-68: Test that signals have a `__get__` function after all.
index 3e91ca3385597a71743c77b8aeb7e7b3e6369426..605def92817c7f5b76136bca59f743fdee32c138 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -100,4 +101,3 @@ class ModelViewTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 70c9e00822deacd2d1bf2aca595498574aa0cbe0..34b8f7f62038b79dafe80eabf28c518c3b22477a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -12,12 +13,26 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(True)
 
-from testbinding import TestObject
-from PySide6.QtCore import QObject, SIGNAL
+# Note: For PYSIDE-2792/testConnectionSignal()), QMetaObject needs to be
+# forcibly created before Connection.
+from PySide6.QtCore import QObject, SIGNAL, Slot, QMetaObject  # noqa: F401
+from testbinding import TestObject, Connection
 
 '''Tests the behaviour of signals with default values when emitted from Python.'''
 
 
+class Receiver(QObject):
+    """Test receiver for PYSIDE-2792 (testConnectionSignal)."""
+
+    def __init__(self, p=None):
+        super().__init__(p)
+        self.received_handle = -1
+
+    @Slot(Connection)
+    def connectionSlot(self, c):
+        self.received_handle = c.handle()
+
+
 class SignalEmissionFromPython(unittest.TestCase):
 
     def setUp(self):
@@ -48,6 +63,15 @@ class SignalEmissionFromPython(unittest.TestCase):
         self.assertEqual(self.one_called, 1)
         self.assertEqual(self.two_called, 2)
 
+    def testConnectionSignal(self):
+        """PYSIDE-2792: Test whether a signal parameter of type 'Connection'
+           clashes with QMetaObject.Connection."""
+        receiver = Receiver()
+        qmetaobject_conn = self.obj1.connectionSignal.connect(receiver.connectionSlot)
+        self.assertTrue(qmetaobject_conn)
+        self.obj1.emitConnectionSignal(42)
+        self.assertEqual(receiver.received_handle, 42)
+
     def testConnectOldStyleEmitVoidSignal(self):
         def callbackOne():
             self.one_called += 1
@@ -93,4 +117,3 @@ class SignalEmissionFromPython(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 5faaa38d48999964e03283645e35e7e161634f22..61e59286fd9facf17bdcb0240a400809239b5068 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 744b8c503b09069102dbf8b0503494b5b999327b..5b8df2f3e921886ac499c67eafa031fab777a9a8 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 4a482c35a12498e6b5895d976376ca87db842b41..e423542a6c460c4aa7bc575438c5d708ee758412 100644 (file)
@@ -1,23 +1,23 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
-import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths  # noqa: E402
 init_test_paths(False)
 
 """
 PYSIDE-2029: Tests that snake_case is isolated from imported modules
 """
 
-from PySide6.QtWidgets import QWidget
+from PySide6.QtWidgets import QWidget  # noqa: E402
 
 
 def test_no_snake_case():
     print(__name__)
     widget = QWidget()
-    check = widget.sizeHint
+    check = widget.sizeHint  # noqa
index 14e03577306f4bfd1f34ac5748d99049f118d56d..bdcd996c4535bfe5e35a63b4deb8f0360668f56c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -15,21 +16,21 @@ PYSIDE-2029: Tests that snake_case is isolated from imported modules
 """
 is_pypy = hasattr(sys, "pypy_version_info")
 
-from PySide6.QtCore import QSize
-from PySide6.QtWidgets import QWidget, QSpinBox
+from PySide6.QtWidgets import QWidget
 if not is_pypy:
-    from __feature__ import snake_case
+    from __feature__ import snake_case  # noqa
 from helper.usesqapplication import UsesQApplication
 
 import snake_case_sub
 
+
 @unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
 class SnakeCaseNoPropagateTest(UsesQApplication):
 
     def testSnakeCase(self):
         # this worked
         widget = QWidget()
-        check = widget.size_hint
+        check = widget.size_hint  # noqa
 
         snake_case_sub.test_no_snake_case()
 
index fe4ec98f72116f112f7a019e497ad50fa3748604..bcf629bf837a85651e6e73faa352b3ef3b06b609 100644 (file)
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
 
 #include "testobject.h"
+#include "connection.h"
 
 #include <QtCore/QDebug>
 
@@ -10,6 +11,12 @@ void TestObject::emitIdValueSignal()
     emit idValue(m_idValue);
 }
 
+void TestObject::emitConnectionSignal(int handle)
+{
+    Connection connection(handle);
+    emit connectionSignal(connection);
+}
+
 void TestObject::emitStaticMethodDoubleSignal()
 {
     emit staticMethodDouble();
index a095a382ebd42d6de2fd51686c44ffb2f1e5fc56..9689589ad3e9b8bf9b77837a52af0f9cc50fa3f8 100644 (file)
@@ -28,6 +28,8 @@ public:
 
 using TypedefValue = IntValue;
 
+class Connection;
+
 class PYSIDETEST_API TestObject : public QObject
 {
     Q_OBJECT
@@ -41,6 +43,7 @@ public:
     void addChild(QObject* c) { m_children.append(c); emit childrenChanged(m_children); }
 
     void emitIdValueSignal();
+    void emitConnectionSignal(int handle);
     void emitStaticMethodDoubleSignal();
 
     void emitSignalWithDefaultValue_void();
@@ -58,6 +61,7 @@ public:
 
 signals:
     void idValue(int newValue);
+    void connectionSignal(const Connection &c);
     void justASignal();
     void staticMethodDouble();
     void childrenChanged(const QList<QObject*>&);
@@ -137,4 +141,3 @@ signals:
 } // Namespace PySideCPP2
 
 #endif // TESTOBJECT_H
-
index 746def83e443f058f761134933f7922ddc52afb1..0115c5894108acdaad0b5691f6776c778a461dfc 100644 (file)
@@ -32,4 +32,3 @@ private:
 };
 
 #endif // TESTVIEW_H
-
index 62f6505dcf243d2eda4a84ba371d3dae6d07655a..d1ad5f349409b4f2e491c3cdcc0c2b3d72698caf 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -18,7 +19,7 @@ is_pypy = hasattr(sys, "pypy_version_info")
 from PySide6.QtCore import QSize
 from PySide6.QtWidgets import QWidget, QSpinBox
 if not is_pypy:
-    from __feature__ import true_property
+    from __feature__ import true_property  # noqa
 from helper.usesqapplication import UsesQApplication
 
 
index d0bdc880b66ed996d4c6968dbf2a091152be1924..969ebb98e965128c3e872505efab23f8ea98be42 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 592d90a8385a63240ca519a545ecab2e1e87211f..f2bbaa61a2c150112be7a0930d6c271552c013a0 100644 (file)
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <typesystem package="testbinding">
     <load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
+    <value-type name="Connection"/>
     <value-type name="IntValue"/>
     <primitive-type name="TypedefValue">
         <!--
@@ -40,7 +41,7 @@
     <namespace-type name="PySideCPP">
         <object-type name="TestObjectWithNamespace"/>
         <object-type name="TestObject2WithNamespace">
-            <add-function signature="__repr__" return-type="PyObject*">
+            <add-function signature="__repr__" return-type="str">
                 <inject-code class="target" position="beginning">
                     %PYARG_0 = Shiboken::String::fromCString("TestObject2WithNamespace(injected_repr)");
                 </inject-code>
index f47ffc6d059504eee27fdfb2eb61ecff709e269a..f23ebce465f31e70b685e7ac8e57df945e4b3647 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -28,4 +29,3 @@ class CheckForVariablesTest(unittest.TestCase):
 
 if __name__ == '__main__':
     unittest.main()
-
index 5540b6e044ef38d876b7a7cebda4648a3f5450e9..2ad4c5b1bd381c067279c4f8b05119efeaa4e5ce 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 existence_test.py
index 8abf302168eaff0cf4e1b427bd2acee0bfa2dbf4..e911f2182161953ae387019c7e7d06641647736e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 init_platform.py
@@ -26,7 +27,7 @@ def qt_build():
     try:
         from PySide6.QtCore import QLibraryInfo
         result = QLibraryInfo.build()
-    except:
+    except Exception:
         pass
     return result
 
@@ -84,26 +85,26 @@ def set_ospaths(build_dir):
 
 set_ospaths(all_build_dir)
 
-import PySide6
+import PySide6  # noqa: E402
 
 all_modules = list("PySide6." + _ for _ in PySide6.__all__)
 
 # now we should be able to do all imports:
 if not have_build_dir:
     sys.path.insert(0, os.path.join(pyside_build_dir, "tests", "pysidetest"))
-import testbinding
+import testbinding  # noqa: E402 F401
 all_modules.append("testbinding")
 
-from shiboken6 import Shiboken
+from shiboken6 import Shiboken  # noqa: E402 F401
 all_modules.append("shiboken6.Shiboken")
 
-from shibokensupport.signature.lib.enum_sig import SimplifyingEnumerator
+from shibokensupport.signature.lib.enum_sig import SimplifyingEnumerator  # noqa: E402
 
 # Make sure not to get .pyc in Python2.
 sourcepath = os.path.splitext(__file__)[0] + ".py"
 
 
-class Formatter(object):
+class Formatter:
     """
     Formatter is formatting the signature listing of an enumerator.
 
@@ -119,21 +120,21 @@ class Formatter(object):
         print(*args, file=self.outfile, **kw) if self.outfile else None
 
     @contextmanager
-    def module(self, mod_name):
-        self.print(f"")
+    def module(self, mod_name, *other):
+        self.print()
         self.print(f"# Module {mod_name}")
-        self.print(f"sig_dict.update({{")
+        self.print("sig_dict.update({")
         yield
         self.print(f'    }}) if "{mod_name}" in sys.modules else None')
 
     @contextmanager
-    def klass(self, class_name, class_str):
+    def klass(self, class_name, class_str, *other):
         self.print()
         self.print(f"# class {self.mod_name}.{class_name}:")
         yield
 
     @contextmanager
-    def function(self, func_name, signature):
+    def function(self, func_name, signature, *other):
         if self.last_level > self.level:
             self.print()
         self.last_level = self.level
@@ -163,6 +164,7 @@ def enum_all():
 LICENSE_TEXT = """
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 """
 
 
@@ -173,7 +175,7 @@ def generate_all():
         fmt = Formatter(outfile)
         enu = SimplifyingEnumerator(fmt)
         lines = f.readlines()
-        license_line = next((lno for lno, line in enumerate(lines)
+        license_line = next((lno for lno, line in enumerate(lines)  # noqa: F841
                              if "$QT_END_LICENSE$" in line))
         fmt.print("#recreate       # uncomment this to enforce generation")
         fmt.print(LICENSE_TEXT)
@@ -187,10 +189,7 @@ def generate_all():
             {build}
 
             There are no default values, no variable names and no self
-            parameter. Only types are present after simplification. The
-            functions 'next' resp. '__next__' are removed to make the output
-            identical for Python 2 and 3. '__div__' is also removed,
-            since it exists in Python 2, only.
+            parameter. Only types are present after simplification.
             """
             '''))
         fmt.print("import sys")
index b7b6b58aab5805187d8afb98befe82bdf42f90ba..055df973f6f2a5e90df629b5c5f3a4b284cf240f 100644 (file)
@@ -1,5 +1,17 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+from bs4 import BeautifulSoup
+from datetime import datetime
+from multiprocessing import Pool
+from textwrap import dedent
+import requests
+import os
+import time
+import re
+import json
+import argparse
 
 """
 scrape_testresults.py
@@ -16,30 +28,15 @@ After the cache has been created, the runtime is substantially smaller.
 
 """
 
-import sys
-if sys.version_info[:2] < (3, 6):
-    print("This program is written for Python 3.6 or higher.")
-    sys.exit(1)
-
 DEMO_URL = ("https://testresults.qt.io/coin/api/results/pyside/pyside-setup/"
             # The above URL part is fixed.
             "30c1193ec56a86b8d0920c325185b9870f96941e/"
             "MacOSMacOS_10_12x86_64MacOSMacOS_10_12x86_64Clangqtci-macos-"
-                "10.12-x86_64-8-425364DebugAndRelease_Release/"
+            "10.12-x86_64-8-425364DebugAndRelease_Release/"
             "d80c5d4547ea2b3d74188bd458955aae39cb32b4/"
             "test_1535865484/"
             "log.txt.gz")
 
-from bs4 import BeautifulSoup
-from datetime import datetime
-from multiprocessing import Pool
-from textwrap import dedent
-import requests
-import os
-import time
-import re
-import json
-import argparse
 
 my_name = __file__ if __file__.endswith(".py") else __file__[:-1]
 test_path = os.path.join(os.path.dirname(__file__), "testresults", "embedded")
@@ -122,7 +119,7 @@ def get_timestamp(text):
         stop_all = True
         raise
     startpos += len(prefix)
-    text = text[startpos : startpos + 80]
+    text = text[startpos: startpos + 80]
     ts = text[:19]
     ts = re.sub(r'[^0-9]', '_', ts)
     # check that it is a valid time stamp
@@ -151,7 +148,7 @@ def write_data(name, text):
             offset = line.index("BEGIN_FILE")
         if "END_FILE" in line:
             stop = idx
-    lines = lines[start : stop]
+    lines = lines[start: stop]
     if offset:
         lines = list(line[offset:] for line in lines)
     # fix the lines - the original has no empty line after "# eof"
@@ -225,7 +222,7 @@ def handle_suburl_tup(idx_n_url_level):
         return  # bad solution, but it stops fast
     idx, n, url, level = idx_n_url_level
     try:
-        ret = handle_suburl(idx, n, url, level)
+        handle_suburl(idx, n, url, level)
         return url, None
     except requests.exceptions.RequestException as e:
         return url, e
@@ -275,7 +272,6 @@ def handle_topurl(url):
         with open(cache_file, 'r') as fp:
             known_urls = json.load(fp)
             work_urls -= set(known_urls)
-    level = 1
     for sub_url in work_urls:
         name = get_name(sub_url)
         if name.endswith("/"):
index 5d81926a118778a9fb73c738378d975e1d58c870..fc0026fa3b240818891c55cf4fc16b6930c081a9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 Supporting isolation of warnings
@@ -47,7 +48,6 @@ def check_warnings():
         if mod:
             reg = getattr(mod, warn_name, None)
             if reg:
-                # XXX We need to filter warnings for Python 2.
                 # This should be avoided by renaming the duplicate folders.
                 for k in reg:
                     if type(k) is tuple and re.match(ignore_re, k[0]):
index 560a0865967da541896fcae6503b4cd6d7d58c0c..f930d7c3b1e782363d10667c3ad9631a7314d7b1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 4f56be348812203d0c0cda9b2bcc8575f0989974..36b905a526ef8cd6ec18df69786c0acec33a4be6 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 1c013ddeaca1f3e7170ed3797719651c3de12210..72d604061aa9313c8d3141d1e1f2a0c3b256f45b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index e27476172543055e3e1268f41124ec15a4cd1808..b01caa952e8fa8c3802f9f9a33fb57b6393fe502 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 80d56a0205cf1084d2a69c83d2038b243fb431cf..49be26382f6dd2c633d31373d751b81d8e274dc7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 657733afb3db214751311d8f122f6316dcb30e22..2de53f8e285ff3e6bc04d9ee246a9c2d289333b5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 77ac621d5a35c890cb573d2b85290322d2c699b4..99dc1bccb44b2ce7bb4e4721c387aaca17d8cb06 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import gc
index b29339ee49450e51647f6b2da43e0eeb0d668843..431c81d1112bdee1b4ffda157a1a4f4161bee148 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index ea3782a91ae5dd6bc9998e877826fa4745b46d8f..61ce4d5b850d29da5f0a2dea18caf722f533fa1b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 2788c1d1af55901eb9e0adedd6cda52116e2f40b..136ffa3d86b25b2badd1cc6b9ae54002749c8cf8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for passing invalid callbacks to QObject.connect'''
 
index 2123e720686a8f8ee8d1f4f2acebe3ddcc81e0f5..7eacab0c4be851e4d5cd16c336f0402a5cbb217e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Connecting lambda to gui signals'''
 
index 23fcdf5fab43e52dea5ed2ad974169faff164f7d..e0e016f4d48e0f12fb722b908e6b7a849165066f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Connecting lambda to signals'''
 
index 666ae7a1333d2e77d65630ca85df69d8c609d450..be9c4c879b5e2239f6af3eaf1905c3be87d4e652 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 295369b7deba84a9476cde848f05cf872b039d92..f1ad4ffd672260ece9feec8d101987feb83427da 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 2338517979b07b6502bcb29d6e8e189a3b25b9a4..fb03dad3ce589d4aca031258ef923afaf0494289 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 from functools import partial
 import os
index d6f44edf859951659e84204a11d94e956c5234c6..c6e23e8354bed868e48848a3de3382495f6b8b31 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -72,7 +73,7 @@ class PyObjectType(UsesQApplication):
         self.called = False
         self.running = True
         o = Sender()
-        o.dummy2.connect(self.mySlot2, Qt.QueuedConnection)
+        o.dummy2.connect(self.mySlot2, Qt.ConnectionType.QueuedConnection)
         o.callDummy2()
         self.app.exec()
         self.assertEqual(self.callCount, 1)
@@ -81,7 +82,7 @@ class PyObjectType(UsesQApplication):
         self.called = False
         self.running = True
         o = Sender()
-        o.dummy2.connect(self.mySlot2, Qt.QueuedConnection)
+        o.dummy2.connect(self.mySlot2, Qt.ConnectionType.QueuedConnection)
         o.callDummy2()
         o.callDummy2()
         self.app.exec()
index a7a26d6f522e115f20a059d2a3285a9192cfc85a..ff189332ed56276bc5bf0ca716f1a40aa8788728 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index a21762b41da2d430fb97ad422fa8c000dc73d64a..50bc9be15b9bd1507fa585ac2bbe76448af03c5f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 9839255ace2068ca589323b51e97ec990304227e..d2211f4a343f3eb14c69e287baa633b8e596605d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test case for QObject.receivers()'''
 
@@ -29,11 +30,11 @@ class TestQObjectReceivers(unittest.TestCase):
         receiver2 = QObject()
         self.assertEqual(sender.receivers(SIGNAL("")), 0)
         sender.destroyed.connect(receiver1.deleteLater)
-        self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 1)
+        old_count = sender.receivers(SIGNAL("destroyed()"))
         sender.destroyed.connect(receiver2.deleteLater)
-        self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 2)
+        self.assertEqual(sender.receivers(SIGNAL("destroyed()")), old_count + 1)
         sender.disconnect(sender, SIGNAL("destroyed()"), receiver2, SLOT("deleteLater()"))
-        self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 1)
+        self.assertEqual(sender.receivers(SIGNAL("destroyed()")), old_count)
         del receiver2
         del receiver1
         del sender
@@ -44,9 +45,9 @@ class TestQObjectReceivers(unittest.TestCase):
         sender = QObject()
         receiver = QObject()
         sender.destroyed.connect(cute_slot)
-        self.assertEqual(sender.receivers(SIGNAL("destroyed( )")), 1)
+        old_count = sender.receivers(SIGNAL("destroyed()"))
         sender.destroyed.connect(receiver.deleteLater)
-        self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 2)
+        self.assertEqual(sender.receivers(SIGNAL("destroyed()")), old_count + 1)
         del sender
         del receiver
         # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
index 9c1121eb871293fbe52c1a92174f629228747d9f..0e8ad2c0bc2d78c4972a8f4cb01ec136325bea14 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for QObject.sender()'''
 
index 1a62b2218baf565f80c1b6f7b243def667619241..0a247fb3c5876fe095679072f4bfe04059678767 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 54b6f4a52fa6dd3142b19fee4219d49ab835577f..be2cccc46eef070abd593f6753fcb5839b5254cb 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index c43c2e549f2fff65a53d20f8b6d1d64390ebe688..ef2f5508249b1ed11face943ea64695c54562fcb 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index fce8014569f93cd93e6288e19c8852b768c56f5f..25c733e4712a27da92e0c322f496909f9d3aae1d 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index fb9debf39d3901a68da9855f4b63b177b1a7050e..a7e5feac1bba111ca107618a7227f037f43da9ab 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index a827131dba3fafb59d85c3acf4b1d334bd5469da..7ec3baba21d8fbae8d01a9bbbb16bd55a34e853e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index cb0df0978d1c0cde1d535e45512675949141fe9a..6959594683d4d60a54d27d2dc33a2b7e281c5265 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 08ca725f8b3ac925c34acea53a75f5389193b254..fdc786ddfe4a0deecf3120f60a10dc8583046a18 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Using self.connect(signal, method)'''
 
index 1ad4bc24c1f5a013735b98a77088b98b96e44311..e0453d8ecc193cd8d61cf247a03f95d69c4dab36 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 31129f7a14e493ecdc4d7eec78d3f29f7d06149a..7d9c0bf918c54ca5f48e8e4c2c9c601f77b596d3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test case for signal to signal connections.'''
 
index 91b1ca9861f604671e9c0cbd08522edaad78e7d9..5b18d78f77f581d2bf95dd857b68d047e2ffb061 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for PYSIDE-1354: Ensure that slots are invoked from the receiver's
 thread context when using derived classes (and thus, a global receiver).'''
index 51d1cea3abea6acf22aef52b091e26fb65edf178..5975c0bd75446cc08f3f9604e4c40224c93512c4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 0a69c1e02f9e96a2d8082247c9c8fbe85c822ba1..296185ccef386674170cce4b788898bdbd1ce032 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -31,7 +32,7 @@ class TestConnectionTypeSupport(unittest.TestCase):
         """Connect signal using a Qt.ConnectionType as argument"""
         obj1 = Sender()
 
-        obj1.foo.connect(self.callback, Qt.DirectConnection)
+        obj1.foo.connect(self.callback, Qt.ConnectionType.DirectConnection)
         self.args = tuple()
         obj1.foo.emit(*self.args)
 
index 5a49b9d1262ba962b41165589370705abe35a5a9..1e027c59ea995024f3e6e86150c3c18109960104 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """Tests covering signal emission and receiving to python slots"""
 
index b31d89c2fce3d6b6380b5aad52b76e07c57de027..c4c6ac256a7826a4f4ac8ac37f948fe97f89188c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """Tests covering signal emission and receiving to python slots"""
 
@@ -14,7 +15,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtCore import QObject, Signal, SIGNAL, QProcess, QTimeLine
+from PySide6.QtCore import QObject, Signal, SIGNAL, QProcess, QTimeLine, Slot
 
 from helper.usesqapplication import UsesQApplication
 
@@ -44,6 +45,18 @@ class Sender(QObject):
     dummy_int = Signal(int)
 
 
+class Receiver(QObject):
+    '''Receiver class'''
+
+    def __init__(self, p=None):
+        super().__init__(p)
+        self.n = 0
+
+    @Slot(int)
+    def intSlot(self, n):
+        self.n = n
+
+
 class PythonSignalToCppSlots(UsesQApplication):
     '''Connect python signals to C++ slots'''
 
@@ -57,10 +70,10 @@ class PythonSignalToCppSlots(UsesQApplication):
         sender.dummy.emit()
         new_dir = timeline.direction()
 
-        if orig_dir == QTimeLine.Forward:
-            self.assertEqual(new_dir, QTimeLine.Backward)
+        if orig_dir == QTimeLine.Direction.Forward:
+            self.assertEqual(new_dir, QTimeLine.Direction.Backward)
         else:
-            self.assertEqual(new_dir, QTimeLine.Forward)
+            self.assertEqual(new_dir, QTimeLine.Direction.Forward)
 
     def testWithArgs(self):
         '''Connect python signals to QTimeLine.setCurrentTime(int)'''
@@ -74,6 +87,18 @@ class PythonSignalToCppSlots(UsesQApplication):
         self.assertEqual(timeline.currentTime(), current + 42)
 
 
+class ConnectWithContext(UsesQApplication):
+    '''Test whether a connection with context QObject passes parameters.'''
+
+    def testIt(self):
+        sender = Sender()
+        receiver = Receiver()
+        context = sender
+        QObject.connect(sender, SIGNAL("dummy_int(int)"), context, receiver.intSlot)
+        sender.dummy_int.emit(42)
+        self.assertEqual(receiver.n, 42)
+
+
 class CppSignalsToCppSlots(UsesQApplication):
     '''Connection between C++ slots and signals'''
 
@@ -92,10 +117,10 @@ class CppSignalsToCppSlots(UsesQApplication):
 
         new_dir = timeline.direction()
 
-        if orig_dir == QTimeLine.Forward:
-            self.assertEqual(new_dir, QTimeLine.Backward)
+        if orig_dir == QTimeLine.Direction.Forward:
+            self.assertEqual(new_dir, QTimeLine.Direction.Backward)
         else:
-            self.assertEqual(new_dir, QTimeLine.Forward)
+            self.assertEqual(new_dir, QTimeLine.Direction.Forward)
 
 
 called = False
@@ -134,8 +159,8 @@ class EmitEnum(UsesQApplication):
         self.arg = None
         p = QProcess()
         p.stateChanged.connect(self.slot)
-        p.stateChanged.emit(QProcess.NotRunning)
-        self.assertEqual(self.arg, QProcess.NotRunning)
+        p.stateChanged.emit(QProcess.ProcessState.NotRunning)
+        self.assertEqual(self.arg, QProcess.ProcessState.NotRunning)
 
 
 if __name__ == '__main__':
index a792e9b0c9a9211f2121711bedc39059f717f671..bc79e2db133a41e0dc37e78f8878b74a34e4b180 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 from enum import Enum
 import os
index d441d4de9087cde88c3fd950a1391184f0631365..511223fcff892cf33155bbc84203c97a4e4af1c3 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 955d5b65b2c9fdc0b72ac806edcd0583dcf8dfa6..d11d1704f5b50bab54fbbba25778451f0cd579bd 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 
 import os
index 5fbb875afb4a7480b85026af09511ab3f02b42d1..0140c7db724766c113d2e42005f27bb51d481ecd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -18,7 +19,7 @@ class Receiver(QObject):
         super().__init__()
         self.result = 0
 
-    @Slot(Qt.Alignment, str)
+    @Slot(Qt.AlignmentFlag, str)
     def handler(self, e, s):
         print('handler', e, "type=", type(e).__name__, s)
         self.result += 1
@@ -31,7 +32,7 @@ class Sender(QObject):
         super().__init__()
 
     def emit_test_sig(self):
-        self.test_sig.emit(Qt.AlignLeft, "bla")
+        self.test_sig.emit(Qt.AlignmentFlag.AlignLeft, "bla")
 
 
 class TestSignalNewEnum(unittest.TestCase):
index 29825fe5009b4f4c8f987bc5e01ab4b7b841e8ec..959ae6f088e85d186df60710addd273cfd263001 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 607f5181317475840d9ae624bfc155b961da409f..777a4d0b05d4966442566ce210b430788d540795 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -74,7 +75,7 @@ class SignalObjectTest(UsesQApplication):
 
     def testConnectionType(self):
         o = MyObject()
-        o.timeout.connect(self.cb, type=Qt.DirectConnection)
+        o.timeout.connect(self.cb, type=Qt.ConnectionType.DirectConnection)
         o.start(100)
         self.app.exec()
         self.assertTrue(self._cb_called)
index e8f08b2d9fb6b7f4cccd8c0a404bdbcb8112f1ef..e235e1beffa59ee2329b217a976e64e68bf3e291 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for signal signature received by QObject::connectNotify().'''
 
@@ -61,9 +62,6 @@ class TestConnectNotifyWithNewStyleSignals(UsesQApplication):
         sender = Obj()
         receiver = QObject()
         sender.connect(SIGNAL('destroyed()'), receiver, SLOT('deleteLater()'))
-        # When connecting to a regular slot, and not a python callback function, QObject::connect
-        # will use the non-cloned method signature, so connectinc to destroyed() will actually
-        # connect to destroyed(QObject*).
         self.assertEqual(sender.signal.methodSignature(), 'destroyed(QObject*)')
 
     def testOldStyleWithPythonCallback(self):
index 01492b333a94773f30ce391e6c84bfc176fc1dd3..96554c31be12fd19d08eefa74674cbce53c48054 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 9d5c7365205bf4f0d8f1ef13f1a50a60b6f318a7..88c97f33db2e003c7c50bd36a60e85b4f94ca572 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Forced disconnection: Delete one end of the signal connection'''
 
index d7bf73e44e24fb9275e74708fa3e33baa75d5be5..b48e4bc29bda624440fbf396ecf78f53a8703090 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """Tests covering signal emission and receiving to python slots"""
 
index d9ea57a61d663475b3b548577fe71966b3cdd63e..a5b6e48efe11fe727eca668f65d5ce58f6b34503 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b773b7c58f044b102559f817db7c4a21d2f79ee9..15c7ed181531e3d3e6179fa88756d375c7225bf9 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 # This is a small script printing out Qt binding class hierarchies
 # for comparison purposes.
@@ -12,8 +13,6 @@
 #
 # meld pyside.list pyqt5.list
 
-import sys
-import pdb
 from inspect import isclass
 
 ignore = ["staticMetaObject",
@@ -47,26 +46,26 @@ def recurse_into(el, obj):
 
 
 if __name__ == '__main__':
-    modules = [ 'QtCore',
-                'QtGui',
-                'QtHelp',
+    modules = ['QtCore',
+               'QtGui',
+               'QtHelp',
                #'QtMultimedia',
-                'QtNetwork',
+               'QtNetwork',
                #'QtOpenGL',
-                'QtScript',
-                'QtScriptTools',
-                'QtSql',
-                'QtSvg',
-                'QtTest',
+               'QtScript',
+               'QtScriptTools',
+               'QtSql',
+               'QtSvg',
+               'QtTest',
                #'QtUiTools',
-                'QtXml',
-                'QtXmlPatterns' ]
+               'QtXml',
+               'QtXmlPatterns']
 
     libraries = ["PySide6", "PyQt5"]
     librarySymbols = {}
-    for l in libraries:
+    for lib in libraries:
         dictionary = []
-        if l == "PyQt5":
+        if lib == "PyQt5":
             import sip
             sip.setapi('QDate', 2)
             sip.setapi('QDateTime', 2)
@@ -77,9 +76,9 @@ if __name__ == '__main__':
             sip.setapi('QVariant', 2)
 
         for m in modules:
-            exec(f"from {l} import {m}", globals(), locals())
+            exec(f"from {lib} import {m}", globals(), locals())
             dictionary += recurse_into(m, eval(m))
-        librarySymbols[l] = dictionary
+        librarySymbols[lib] = dictionary
 
     print("PyQt5: ", len(librarySymbols["PyQt5"]), " PySide6: ", len(librarySymbols["PySide6"]))
 
index 5e7412bf5390c3f4658e54de684e99373af4cd47..fbfea010e7422c8283e93c4e5b250f4ed98746e1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 271f8eebdc509826d8955504a3b9c31c9381a5f6..c189922ed2af8abfbb48103ccb9fe3991bf97195 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
     Extensive manual test of pyside6-android-deploy
index ec575e923b24673bbc4a906feaeb51aef8999d1e..120c54af3fd28a39d26760a6caad45ca817ff8cc 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import importlib
 import os
@@ -123,7 +124,7 @@ class TestPySide6AndroidDeployWidgets(DeployTestBase):
 
         # test config file contents
         config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
-        self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
+        self.assertTrue(config_obj.get_value("app", "input_file").endswith("main.py"))
         self.assertEqual(config_obj.get_value("python", "android_packages"),
                          "buildozer==1.5.0,cython==0.29.33")
         self.assertEqual(config_obj.get_value("android", "wheel_pyside"),
@@ -168,11 +169,26 @@ class TestPySide6AndroidDeployWidgets(DeployTestBase):
         self.buildozer_config.unlink()
 
     def test_errors(self, mock_extract_jar):
-        # test if error raises for non existing NDK
-        with self.assertRaises(FileNotFoundError) as context:
+        # test when no shiboken wheel is passed
+        with self.assertRaises(RuntimeError) as context:
+            self.android_deploy.main(name="android_app", pyside_wheel=self.pyside_wheel,
+                                     ndk_path=self.ndk_path, force=True)
+        self.assertTrue("Unable to find shiboken6 Android wheel" in str(context.exception))
+
+        # test when no PySide wheel is passed
+        with self.assertRaises(RuntimeError) as context:
             self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
-                                     pyside_wheel=self.pyside_wheel, force=True)
-        self.assertTrue("Unable to find Android NDK" in str(context.exception))
+                                     ndk_path=self.ndk_path, force=True)
+        self.assertTrue("Unable to find PySide6 Android wheel" in str(context.exception))
+
+        # test if wheel name is correct
+        pyside_wheel_temp = Path("/tmp/"
+                                 "PySide6-6.8.0.1+commercial-6.8.0.1-cp311-cp311-android_fake.whl")
+        with self.assertRaises(RuntimeError) as context:
+            self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
+                                     pyside_wheel=pyside_wheel_temp, ndk_path=self.ndk_path,
+                                     force=True)
+        self.assertTrue("PySide wheel corrupted" in str(context.exception))
 
         # test when cwd() is not project_dir
         os.chdir(self.current_dir)
@@ -181,6 +197,33 @@ class TestPySide6AndroidDeployWidgets(DeployTestBase):
                                      pyside_wheel=self.pyside_wheel, init=True, force=True)
         self.assertTrue("For Android deployment to work" in str(context.exception))
 
+    @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
+    @patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
+    @patch("deploy_lib.android.android_config.AndroidConfig._find_dependent_qt_modules")
+    @patch("deploy_lib.android.android_config.find_qtlibs_in_wheel")
+    @patch("deploy_lib.android.android_config.download_android_ndk")
+    def test_no_ndk_passed(self, mock_ndk, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist,
+                           mock_find_jars, mock_extract_jar):
+        jar_dir = "tmp/jar/PySide6/jar"
+        mock_extract_jar.return_value = Path(jar_dir)
+        mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib"
+        mock_extraqtmodules.return_value = []
+        mock_recipes_exist.return_value = True
+        jars, init_classes = ["/tmp/jar/PySide6/jar/Qt6Android.jar",
+                              "/tmp/jar/PySide6/jar/Qt6AndroidBindings.jar"], []
+        mock_find_jars.return_value = jars, init_classes
+        mock_ndk.return_value = Path("/tmp/android_ndk")
+
+        self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
+                                 pyside_wheel=self.pyside_wheel,
+                                 init=True, force=True,
+                                 keep_deployment_files=True)
+
+        # test config file contents
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+        self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), '')
+        self.assertEqual(config_obj.get_value("buildozer", "ndk_path"), "/tmp/android_ndk")
+
 
 @patch("deploy_lib.config.run_qmlimportscanner")
 @patch("deploy_lib.android.android_config.extract_and_copy_jar")
index 40afc7f5cf574cd46809272cd710a76e95d0c3f6..d15795f4d0c55b845c2e49fe0076c8c16d749d3e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
     Extensive manual test of pyside6-deploy
index f84eec1eb7e535bf722e2f0914ece48fda29f522..a5d5bd25c0137e14c6743e50b367e3f7b2d50a0a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import unittest
 import tempfile
@@ -14,6 +15,7 @@ from unittest import mock
 
 sys.path.append(os.fspath(Path(__file__).resolve().parents[2]))
 from init_paths import init_test_paths, _get_qt_lib_dir  # noqa: E402
+
 init_test_paths(False)
 
 
@@ -63,7 +65,7 @@ class DeployTestBase(LongSortedOptionTest):
         cls.deploy_lib = importlib.import_module("deploy_lib")
         cls.deploy = importlib.import_module("deploy")
         sys.modules["deploy"] = cls.deploy
-        files_to_ignore = [".cpp.o", ".qsb", ".webp"]
+        files_to_ignore = [".cpp.o", ".qsb"]
         cls.dlls_ignore_nuitka = " ".join([f"--noinclude-dlls=*{file}"
                                            for file in files_to_ignore])
 
@@ -100,11 +102,11 @@ class TestPySide6DeployWidgets(DeployTestBase):
         self.deployment_files = self.temp_example_widgets / "deployment"
         # All the plugins included. This is different from plugins_nuitka, because Nuitka bundles
         # some plugins by default
-        self.all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
+        self.all_plugins = ["egldeviceintegrations", "generic", "iconengines",
                             "imageformats", "platforminputcontexts", "platforms",
-                            "platforms/darwin", "platformthemes", "styles", "xcbglintegrations"]
+                            "platformthemes", "styles", "xcbglintegrations"]
         # Plugins that needs to be passed to Nuitka
-        plugins_nuitka = ("accessiblebridge,platforminputcontexts,platforms/darwin")
+        plugins_nuitka = ("platforminputcontexts")
         self.expected_run_cmd = (
             f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
             f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
@@ -147,11 +149,11 @@ class TestPySide6DeployWidgets(DeployTestBase):
 
         # # test config file contents
         config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
-        self.assertEqual(config_obj.get_value("app", "input_file"), "tetrix.py")
-        self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
+        self.assertTrue(config_obj.get_value("app", "input_file").endswith("tetrix.py"))
+        self.assertTrue(config_obj.get_value("app", "project_dir").endswith("tetrix"))
         self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
         self.assertEqual(config_obj.get_value("python", "packages"),
-                         "Nuitka==2.3.2")
+                         "Nuitka==2.5.1")
         self.assertEqual(config_obj.get_value("qt", "qml_files"), "")
         equ_base = "--quiet --noinclude-qt-translations"
         equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
@@ -174,6 +176,65 @@ class TestPySide6DeployWidgets(DeployTestBase):
             self.deploy.main(main_file=fake_main_file, config_file=self.config_file)
         self.assertTrue("Directory does not contain main.py file." in str(context.exception))
 
+    def testStandaloneMode(self, mock_plugins):
+        mock_plugins.return_value = self.all_plugins
+        # remove --onefile from self.expected_run_cmd and replace it with --standalone
+        self.expected_run_cmd = self.expected_run_cmd.replace(" --onefile", " --standalone")
+        # test standalone mode
+        original_output = self.deploy.main(self.main_file, mode="standalone", dry_run=True,
+                                           force=True)
+
+        self.assertEqual(original_output, self.expected_run_cmd)
+
+    @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+    def testExtraModules(self, mock_sitepackages, mock_plugins):
+        mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+        mock_plugins.return_value = self.all_plugins
+        init_result = self.deploy.main(self.main_file, extra_modules_grouped="QtNetwork,QtOpenGL",
+                                       init=True, force=True)
+        self.assertEqual(init_result, None)
+        self.deploy.main(config_file=self.config_file, dry_run=True, force=True)
+
+        # test config file contents
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+        expected_modules = {"Core", "Gui", "Widgets", "Network", "OpenGL"}
+        if sys.platform != "win32":
+            expected_modules.add("DBus")
+        obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
+        self.assertEqual(obtained_modules, expected_modules)
+        self.config_file.unlink()
+
+    @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+    def testExtraIgnoreDirs(self, mock_sitepackages, mock_plugins):
+        # create a directory to ignore
+        ignore_dir = self.temp_example_widgets / "ignore_dir"
+        ignore_dir.mkdir()
+        ignore_file = ignore_dir / "test_ignore.py"
+        ignore_file.write_text("from PySide6 import QtNetwork")
+
+        # rename the .pyproject file inside the example directory
+        project_file = self.temp_example_widgets / "tetrix.pyproject"
+        project_file.rename(self.temp_example_widgets / "tetrix.pyproject.bak")
+
+        mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+        mock_plugins.return_value = self.all_plugins
+        init_result = self.deploy.main(self.main_file, extra_ignore_dirs="ignore_dir",
+                                       init=True, force=True)
+        self.assertEqual(init_result, None)
+        self.deploy.main(config_file=self.config_file, dry_run=True, force=True)
+
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+        expected_modules = {"Core", "Gui", "Widgets"}
+        if sys.platform != "win32":
+            expected_modules.add("DBus")
+        obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
+        self.assertEqual(obtained_modules, expected_modules)
+        self.config_file.unlink()
+
+        #undo rename of project file
+        project_file = self.temp_example_widgets / "tetrix.pyproject.bak"
+        project_file.rename(self.temp_example_widgets / "tetrix.pyproject")
+
 
 @unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
                  "Test only works on macOS version 12+")
@@ -196,14 +257,13 @@ class TestPySide6DeployQml(DeployTestBase):
 
         # All the plugins included. This is different from plugins_nuitka, because Nuitka bundles
         # some plugins by default
-        self.all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
-                            "imageformats", "networkaccess", "networkinformation",
-                            "platforminputcontexts", "platforms", "platforms/darwin",
-                            "platformthemes", "qmltooling", "scenegraph", "tls",
+        self.all_plugins = ["egldeviceintegrations", "generic", "iconengines",
+                            "imageformats", "networkinformation",
+                            "platforminputcontexts", "platforms",
+                            "platformthemes", "qmltooling", "tls",
                             "xcbglintegrations"]
         # Plugins that needs to be passed to Nuitka
-        plugins_nuitka = ("accessiblebridge,networkaccess,networkinformation,platforminputcontexts,"
-                          "platforms/darwin,qml,qmltooling,scenegraph")
+        plugins_nuitka = ("networkinformation,platforminputcontexts,qml,qmltooling")
         self.expected_run_cmd = (
             f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
             f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
@@ -253,11 +313,11 @@ class TestPySide6DeployQml(DeployTestBase):
 
         # test config file contents
         config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
-        self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
-        self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
+        self.assertTrue(config_obj.get_value("app", "input_file").endswith("main.py"))
+        self.assertTrue(config_obj.get_value("app", "project_dir").endswith("editingmodel"))
         self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
         self.assertEqual(config_obj.get_value("python", "packages"),
-                         "Nuitka==2.3.2")
+                         "Nuitka==2.5.1")
         self.assertEqual(config_obj.get_value("qt", "qml_files"), "main.qml,MovingRectangle.qml")
         equ_base = "--quiet --noinclude-qt-translations"
         equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
@@ -266,7 +326,8 @@ class TestPySide6DeployQml(DeployTestBase):
             config_obj.get_value("qt", "excluded_qml_plugins"),
             "QtCharts,QtQuick3D,QtSensors,QtTest,QtWebEngine",
         )
-        expected_modules = {"Core", "Gui", "Qml", "Quick", "Network", "OpenGL", "QmlModels"}
+        expected_modules = {"Core", "Gui", "Qml", "Quick", "Network", "OpenGL", "QmlModels",
+                            "QmlWorkerScript", "QmlMeta"}
         if sys.platform != "win32":
             expected_modules.add("DBus")
         obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
@@ -307,10 +368,10 @@ class TestPySide6DeployWebEngine(DeployTestBase):
     @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
     def testWebEngineQuickDryRun(self, mock_sitepackages, mock_plugins):
         mock_sitepackages.return_value = Path(_get_qt_lib_dir())
-        all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
-                       "imageformats", "networkaccess", "networkinformation",
-                       "platforminputcontexts", "platforms", "platforms/darwin",
-                       "platformthemes", "qmltooling", "scenegraph", "tls",
+        all_plugins = ["egldeviceintegrations", "generic", "iconengines",
+                       "imageformats", "networkinformation",
+                       "platforminputcontexts", "platforms",
+                       "platformthemes", "qmltooling", "tls",
                        "xcbglintegrations"]
         mock_plugins.return_value = all_plugins
         # this test case retains the QtWebEngine dlls
@@ -319,8 +380,7 @@ class TestPySide6DeployWebEngine(DeployTestBase):
         main_file = self.temp_example_webenginequick / "quicknanobrowser.py"
         deployment_files = self.temp_example_webenginequick / "deployment"
         # Plugins that needs to be passed to Nuitka
-        plugins_nuitka = ("accessiblebridge,networkaccess,networkinformation,platforminputcontexts,"
-                          "platforms/darwin,qml,qmltooling,scenegraph")
+        plugins_nuitka = ("networkinformation,platforminputcontexts,qml,qmltooling")
         qml_files = [
             "ApplicationRoot.qml",
             "BrowserDialog.qml",
@@ -381,20 +441,148 @@ class TestPySide6DeployWebEngine(DeployTestBase):
 
         # test config file contents
         config_obj = self.deploy_lib.BaseConfig(config_file=config_file)
-        self.assertEqual(config_obj.get_value("app", "input_file"), "quicknanobrowser.py")
+        self.assertTrue(config_obj.get_value("app", "input_file").endswith("quicknanobrowser.py"))
         self.assertEqual(config_obj.get_value("qt", "qml_files"), ",".join(qml_files))
         self.assertEqual(
             config_obj.get_value("qt", "excluded_qml_plugins"),
             "QtCharts,QtQuick3D,QtSensors,QtTest",
         )
         expected_modules = {"Core", "Gui", "Quick", "Qml", "WebEngineQuick", "Network", "OpenGL",
-                            "Positioning", "WebEngineCore", "WebChannel", "WebChannelQuick",
-                            "QmlModels"}
+                            "QmlModels", "QmlWorkerScript", "QmlMeta", "WebEngineCore",
+                            "Positioning", "WebChannelQuick", "WebChannel"}
         if sys.platform != "win32":
             expected_modules.add("DBus")
         obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
         self.assertEqual(obtained_modules, expected_modules)
 
 
+@unittest.skipIf(sys.platform != "win32", "Test only works on Windows")
+class TestLongCommand(DeployTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        example_qml = cls.example_root / "qml" / "editingmodel"
+        cls.temp_example_qml = Path(
+            shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
+        ).resolve()
+
+    def setUp(self):
+        os.chdir(self.temp_example_qml)
+        self.main_file = self.temp_example_qml / "main.py"
+
+    @patch('deploy_lib.nuitka_helper.os.remove')
+    @patch("deploy_lib.config.run_qmlimportscanner")
+    @patch('deploy.DesktopConfig.qml_files', new_callable=mock.PropertyMock)
+    def test_main_with_mocked_qml_files(self, mock_qml_files, mock_qmlimportscanner, mock_remove):
+        mock_qmlimportscanner.return_value = ["QtQuick"]
+        mock_qml_files.return_value = [self.temp_example_qml / "MovingRectangle.qml"
+                                       for _ in range(500)]
+
+        command_str = self.deploy.main(self.main_file, force=True, keep_deployment_files=True,
+                                       dry_run=True)
+        mock_remove.assert_called_once()
+
+        # check if command_str ends with deploy_main.py
+        self.assertTrue(command_str.endswith("deploy_main.py"))
+
+        # check if deploy_main.py starts with # nuitka-project:
+        with open(self.temp_example_qml / "deploy_main.py", "r") as file:
+            # check if 516 lines start with # nuitka-project:
+            self.assertEqual(len([line for line in file.readlines()
+                                  if line.startswith("# nuitka-project:")]), 516)
+
+
+@unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
+                 "Test only works on macOS version 12+")
+@patch("deploy_lib.config.QtDependencyReader.find_plugin_dependencies")
+class EmptyDSProjectTest(DeployTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+        # setup a test DS Python project
+        base_path = Path(cls.temp_dir) / "PythonProject"
+
+        project_name = "TestProject"
+        files = [
+            base_path / "Python" / "autogen" / "settings.py",
+            base_path / "Python" / "autogen" / "resources.py",
+            base_path / "Python" / "main.py",
+            base_path / project_name / "test.qml",
+            base_path / f"{project_name}Content" / "test.qml",
+            base_path / f"{project_name}.qmlproject",
+            base_path / f"{project_name}.qmlproject.qtds",
+            base_path / f"{project_name}.qrc"
+        ]
+
+        # Create the files
+        for file in files:
+            file.parent.mkdir(parents=True, exist_ok=True)
+            file.touch(exist_ok=True)
+
+        cls.temp_example = base_path
+
+    def setUp(self):
+        os.chdir(self.temp_example)
+        self.temp_example = self.temp_example.resolve()
+        self.main_file = self.temp_example / "Python" / "main.py"
+        self.deployment_files = self.temp_example / "Python" / "deployment"
+
+        self.expected_run_cmd = (
+            f"{sys.executable} -m nuitka {self.main_file} --follow-imports"
+            f" --enable-plugin=pyside6 --output-dir={self.deployment_files} --quiet"
+            f" --noinclude-qt-translations"
+            f" {self.dlls_ignore_nuitka}"
+            f" --noinclude-dlls=*/qml/QtQuickEffectMaker/*"
+            f" --include-qt-plugins=qml"
+        )
+
+        if sys.platform != "win32":
+            self.expected_run_cmd += (
+                " --noinclude-dlls=libQt6Charts* --noinclude-dlls=libQt6Quick*"
+                " --noinclude-dlls=libQt6Quick3D* --noinclude-dlls=libQt6Sensors*"
+                " --noinclude-dlls=libQt6Test* --noinclude-dlls=libQt6WebEngine*"
+            )
+        else:
+            self.expected_run_cmd += (
+                " --noinclude-dlls=Qt6Charts* --noinclude-dlls=Qt6Quick*"
+                " --noinclude-dlls=Qt6Quick3D* --noinclude-dlls=Qt6Sensors*"
+                " --noinclude-dlls=Qt6Test* --noinclude-dlls=Qt6WebEngine*"
+            )
+
+        if sys.platform.startswith("linux"):
+            self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)} --onefile"
+        elif sys.platform == "darwin":
+            self.expected_run_cmd += (f" --macos-app-icon={str(self.macos_icon)}"
+                                      " --macos-create-app-bundle --standalone")
+        elif sys.platform == "win32":
+            self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)} --onefile"
+
+        if is_pyenv_python():
+            self.expected_run_cmd += " --static-libpython=no"
+
+        self.config_file = self.temp_example / "Python" / "pysidedeploy.spec"
+
+    def testDryRun(self, mock_plugins):
+        with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:  # noqa: F841
+            original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
+            self.assertEqual(self.expected_run_cmd, original_output)
+
+    @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+    def testConfigFile(self, mock_sitepackages, mock_plugins):
+        mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+        # create config file
+        with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:  # noqa: F841
+            init_result = self.deploy.main(self.main_file, init=True, force=True)
+            self.assertEqual(init_result, None)
+
+        # test config file contents
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+
+        self.assertTrue(config_obj.get_value("app", "input_file").endswith("main.py"))
+        self.assertTrue(config_obj.get_value("app", "project_dir").endswith("PythonProject"))
+        self.config_file.unlink()
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/sources/pyside6/tests/tools/pyside6-project/CMakeLists.txt b/sources/pyside6/tests/tools/pyside6-project/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2e43957
--- /dev/null
@@ -0,0 +1 @@
+PYSIDE_TEST(test_pyside6_project.py)
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad.qmlproject b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad.qmlproject
new file mode 100644 (file)
index 0000000..0907159
--- /dev/null
@@ -0,0 +1,58 @@
+// prop: json-converted
+// prop: auto-generated
+
+import QmlProject
+
+Project {
+    mainFile: "DrumpadContent/App.qml"
+    mainUiFile: "DrumpadContent/MainScreen.ui.qml"
+    enableCMakeGeneration: false
+    enablePythonGeneration: false
+    widgetApp: true
+    importPaths: [ "." ]
+
+    qdsVersion: "4.5"
+    quickVersion: "6.7"
+    qt6Project: true
+    qtForMCUs: false
+
+    multilanguageSupport: true
+    primaryLanguage: "en"
+    supportedLanguages: [ "en" ]
+
+    Environment {
+        QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1"
+        QT_AUTO_SCREEN_SCALE_FACTOR: "1"
+        QT_ENABLE_HIGHDPI_SCALING: "0"
+        QT_LOGGING_RULES: "qt.qml.connections=false"
+        QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf"
+    }
+
+    QmlFiles {
+        directory: "Drumpad"
+    }
+
+    QmlFiles {
+        directory: "DrumpadContent"
+    }
+
+    QmlFiles {
+        directory: "Generated"
+    }
+
+    Files {
+        files: [
+            "qtquickcontrols2.conf"
+        ]
+    }
+
+    Files {
+        directory: "Drumpad"
+        filter: "qmldir"
+    }
+
+    Files {
+        directory: "DrumpadContent"
+        filter: "*.ttf;*.otf"
+    }
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad.qrc b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad.qrc
new file mode 100644 (file)
index 0000000..6984833
--- /dev/null
@@ -0,0 +1,12 @@
+
+<RCC>
+    <qresource>
+        <file>Drumpad.qmlproject</file>
+        <file>Drumpad/TestComponent.qml</file>
+        <file>DrumpadContent/App.qml</file>
+        <file>DrumpadContent/MainScreen.ui.qml</file>
+        <file>qtquickcontrols2.conf</file>
+        <file>Resources/resource1.txt</file>
+        <file>Resources/resource2.txt</file>
+    </qresource>
+</RCC>
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad/TestComponent.qml b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad/TestComponent.qml
new file mode 100644 (file)
index 0000000..95a1c77
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Text {
+    text: "The quick brown fox"
+    color: "#303030"
+    font.family: "Ubuntu"
+    font.pixelSize: 28
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad/qmldir b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Drumpad/qmldir
new file mode 100644 (file)
index 0000000..6715d17
--- /dev/null
@@ -0,0 +1,3 @@
+module Drumpad
+
+TestComponent 1.0 TestComponent.qml
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/App.qml b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/App.qml
new file mode 100644 (file)
index 0000000..fc29cd9
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import Drumpad
+
+Window {
+    id: root
+
+    height: 800
+    title: "Drumpad"
+    visible: true
+    width: 1200
+
+    MainScreen {
+        id: mainScreen
+
+        anchors.fill: parent
+    }
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/MainScreen.ui.qml b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/MainScreen.ui.qml
new file mode 100644 (file)
index 0000000..541516f
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
+It is supposed to be strictly declarative and only uses a subset of QML. If you edit
+this file manually, you might introduce QML code that is not supported by Qt Design Studio.
+Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
+*/
+import QtQuick
+import QtQuick.Controls
+import Drumpad
+
+Rectangle {
+    id: root
+
+    TestComponent {
+    }
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/qmldir b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/DrumpadContent/qmldir
new file mode 100644 (file)
index 0000000..cdeff96
--- /dev/null
@@ -0,0 +1,3 @@
+module DrumpadContent
+App 1.0 App.qml
+MainScreen 1.0 MainScreen.ui.qml
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/.pyproject b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/.pyproject
new file mode 100644 (file)
index 0000000..a5f654c
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "files": ["main.py", "autogen/settings.py"]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/autogen/settings.py b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/autogen/settings.py
new file mode 100644 (file)
index 0000000..7acbccc
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+# This file is automatically generated by Qt Design Studio.
+
+import os
+import sys
+from pathlib import Path
+
+from PySide6.QtQml import QQmlApplicationEngine
+
+project_root = Path(__file__).parent.parent.parent
+
+
+def setup_qt_environment(qml_engine: QQmlApplicationEngine):
+    """
+    Load the QML application. Import the compiled resources when the application is deployed.
+    """
+    qml_app_url = "DrumpadContent/App.qml"
+
+    if "__compiled__" in globals():
+        # Application has been deployed using pyside6-deploy
+        try:
+            import autogen.resources  # noqa: F401
+        except ImportError:
+            resource_file = Path(__file__).parent / "resources.py"
+            print(
+                f"Error: No compiled resources found in {resource_file.absolute()}\n"
+                f"Please compile the resources using pyside6-rcc or pyside6-project build",
+                file=sys.stderr,
+            )
+            sys.exit(1)
+
+        qml_engine.addImportPath(":/")
+        qml_engine.load(f":/{qml_app_url}")
+        return
+
+    qml_engine.addImportPath(str(project_root.absolute()))
+    os.environ["QT_QUICK_CONTROLS_CONF"] = str(project_root / "qtquickcontrols2.conf")
+    qml_engine.load(str(project_root / qml_app_url))
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/main.py b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/main.py
new file mode 100644 (file)
index 0000000..a9a0cf6
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import sys
+
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from autogen.settings import setup_qt_environment
+
+
+def main():
+    app = QGuiApplication(sys.argv)
+    engine = QQmlApplicationEngine()
+
+    setup_qt_environment(engine)
+
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    ex = app.exec()
+    del engine
+    return ex
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Resources/resource1.txt b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Resources/resource1.txt
new file mode 100644 (file)
index 0000000..9cec92f
--- /dev/null
@@ -0,0 +1 @@
+Resource 1
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Resources/resource2.txt b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Resources/resource2.txt
new file mode 100644 (file)
index 0000000..2f1b34d
--- /dev/null
@@ -0,0 +1 @@
+Resource 2
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/qtquickcontrols2.conf b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/qtquickcontrols2.conf
new file mode 100644 (file)
index 0000000..87a95d0
--- /dev/null
@@ -0,0 +1,6 @@
+; This file can be edited to change the style of the application
+; Read "Qt Quick Controls 2 Configuration File" for details:
+; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
+
+[Controls]
+Style=Basic
diff --git a/sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py b/sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py
new file mode 100644 (file)
index 0000000..6495abb
--- /dev/null
@@ -0,0 +1,158 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import contextlib
+import io
+import os
+import shutil
+import sys
+import unittest
+from unittest import mock
+from unittest import TestCase
+import tempfile
+import importlib
+from pathlib import Path
+
+sys.path.append(str(Path(__file__).resolve().parents[2]))
+from init_paths import init_test_paths
+
+init_test_paths(False)
+
+
+class PySide6ProjectTestBase(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.pyside_root = Path(__file__).parents[5].resolve()
+        cls.example_root = cls.pyside_root / "examples"
+        tools_path = cls.pyside_root / "sources" / "pyside-tools"
+        if tools_path not in sys.path:
+            sys.path.append(str(tools_path))
+        cls.project = importlib.import_module("project")
+        cls.project_lib = importlib.import_module("project_lib")
+        cls.temp_dir = Path(tempfile.mkdtemp())
+        cls.current_dir = Path.cwd()
+        # print no outputs to stdout
+        sys.stdout = mock.MagicMock()
+
+    @classmethod
+    def tearDownClass(cls):
+        os.chdir(cls.current_dir)
+        shutil.rmtree(cls.temp_dir)
+
+    def setUp(self):
+        os.chdir(self.temp_dir)
+
+
+class TestPySide6ProjectDesignStudio(PySide6ProjectTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        example_drumpad = Path(__file__).parent / "example_drumpad"
+        cls.temp_example_drumpad = Path(
+            shutil.copytree(example_drumpad, cls.temp_dir / "drumpad")
+        ).resolve()
+
+    def testDrumpadExample(self):
+        # This test compiles the .qrc file into a .py file and checks whether the compilation is
+        # carried out only when required
+        compiled_resources_path = self.temp_example_drumpad / "Python" / "autogen" / "resources.py"
+        resources_path = self.temp_example_drumpad / "Drumpad.qrc"
+        requires_rebuild = self.project_lib.utils.requires_rebuild
+
+        self.assertFalse(compiled_resources_path.exists())
+
+        os.chdir(self.temp_example_drumpad / "Python")
+        self.project.main(mode="build")
+
+        self.assertTrue(compiled_resources_path.exists())
+        self.assertFalse(requires_rebuild([resources_path], compiled_resources_path))
+
+        # Refresh the modification timestamp of the .qrc resources file so that it is considered
+        # as modified
+        resources_path.touch()
+
+        self.assertTrue(requires_rebuild([resources_path], compiled_resources_path))
+
+        self.project.main(mode="build")
+
+        self.assertFalse(requires_rebuild([resources_path], compiled_resources_path))
+
+        # Refresh the modification timestamp of one of the resources files
+        list((self.temp_example_drumpad / "Resources").glob("*.txt"))[0].touch()
+        self.assertTrue(requires_rebuild([resources_path], compiled_resources_path))
+
+        self.project.main(mode="clean")
+
+        self.assertFalse(compiled_resources_path.exists())
+
+
+class TestPySide6ProjectNew(PySide6ProjectTestBase):
+    def testNewUi(self):
+        with self.assertRaises(SystemExit) as context:
+            self.project.main(mode="new-ui", file="TestProject")
+        test_project_path = Path("TestProject")
+        self.assertTrue((test_project_path / "TestProject.pyproject").exists())
+        self.assertTrue((test_project_path / "mainwindow.ui").exists())
+        self.assertTrue((test_project_path / "main.py").exists())
+        self.assertEqual(context.exception.code, 0)
+        shutil.rmtree(test_project_path)
+
+    def testRaiseErrorOnExistingProject(self):
+        with self.assertRaises(SystemExit) as context:
+            self.project.main(mode="new-ui", file="TestProject")
+        self.assertEqual(context.exception.code, 0)
+        error_message = io.StringIO()
+        with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
+            self.project.main(mode="new-ui", file="TestProject")
+        self.assertEqual(context.exception.code, -1)
+        self.assertTrue(error_message.getvalue())  # some error message is printed
+        shutil.rmtree(self.temp_dir / "TestProject")
+
+    def testNewQuick(self):
+        with self.assertRaises(SystemExit) as context:
+            self.project.main(mode="new-quick", file="TestProject")
+        test_project_path = Path("TestProject")
+        self.assertTrue((test_project_path / "TestProject.pyproject").exists())
+        self.assertTrue((test_project_path / "main.qml").exists())
+        self.assertTrue((test_project_path / "main.py").exists())
+        self.assertEqual(context.exception.code, 0)
+        shutil.rmtree(test_project_path)
+
+    def testNewWidget(self):
+        with self.assertRaises(SystemExit) as context:
+            self.project.main(mode="new-widget", file="TestProject")
+        test_project_path = Path("TestProject")
+        self.assertTrue((test_project_path / "TestProject.pyproject").exists())
+        self.assertTrue((test_project_path / "main.py").exists())
+        self.assertEqual(context.exception.code, 0)
+        shutil.rmtree(test_project_path)
+
+    def testRaiseErrorWhenNoProjectNameIsSpecified(self):
+        error_message = io.StringIO()
+        with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
+            self.project.main(mode="new-widget", file="")
+        self.assertEqual(context.exception.code, 1)
+        self.assertTrue(error_message.getvalue())  # some error message is printed
+
+
+class TestPySide6ProjectRun(PySide6ProjectTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        example_widgets = cls.example_root / "widgets" / "widgets" / "tetrix"
+        cls.temp_example_tetrix = Path(
+            shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
+        ).resolve()
+
+    def testRunEmptyProject(self):
+        project_folder = self.temp_dir / "TestProject"
+        project_folder.mkdir()
+        os.chdir(project_folder)
+        error_message = io.StringIO()
+        with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
+            self.project.main(mode="run")
+        self.assertEqual(context.exception.code, 1)
+        self.assertTrue(error_message.getvalue())  # some error message is printed
+
+
+if __name__ == "__main__":
+    unittest.main()
index fdaf3d471fc15cb695d261b7a514173a92ace1c2..6015ebc8894ed9b8bd1ed471c92a57eeaa586498 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2018 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """Test for pyside6-qml
 
index e85be28f39a4714634c1cd6c2bf9fe0e2a5735dd..fb91beac80c057fa4923b28ff40f499fe46fef04 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Function to print a colored line to terminal'''
 
index 500735c3ab1da85ffc22893bcd6aba3cd9feae91..b495eaead8ba060fbc5c6ea89d1d8d7c7d9a4c3e 100644 (file)
@@ -1,10 +1,11 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 
 
-class BasicPySlotCase(object):
+class BasicPySlotCase:
     '''Base class that tests python slots and signal emissions.
 
     Python slots are defined as any callable passed to QObject.connect().
index cfb665640afa279d38754c205a1665e10ced51f5..ee95d9a0f1094b54dfb6b96feba57be51b975127 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Helper metaclass do 'decorate' docstrings from base test case classes'''
 
@@ -58,7 +59,7 @@ class DocModifier(type):
 if __name__ == '__main__':
 
     # tests
-    class BaseTest(object):
+    class BaseTest:
         __metaclass__ = DocModifier
 
         def testBase(self):
index f80753f1f2b3b6170b2a86c42faee289241f59a6..ba742099ebd20bf4fa68e3b199b7b863950f02ca 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Helper classes and functions'''
 
index d9250a9e0ac8d9dd3911c62595e16b9d4ae52ea5..4984ad2c0527c7922080ec3003136c7b55b77aa7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Helper classes and functions'''
 
index 3d9a4217c976eacb85081eed93d9a7a4724bacd8..86bc8df7d77f7cc8bb74cd3776bcf7e49b8eb740 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Helper classes and functions'''
 
index f62e320f4b684f61c60d8f27df48149b125f3f2d..44723d392779e2afd381d5c2a71cae2917f0b298 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Helper classes and functions'''
 
index 4e6be4881a3d36db8ca8f9fe259e6055ef1514b1..4545ef20d9c659c32c257918adb64b96bf3b39b8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import http.server as BaseHTTPServer
 import os
@@ -7,7 +8,6 @@ import random
 import select
 import sys
 import socketserver as SocketServer
-import tempfile
 import threading
 
 sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "util"))
@@ -37,7 +37,7 @@ class TestSecureHandler(BaseHTTPServer.BaseHTTPRequestHandler):
 
     def do_GET(self):
         self.send_head()
-        self.wfile.write(py3k.b(TestHandler.DATA))
+        self.wfile.write(TestHandler.DATA)
 
     def do_HEAD(self):
         self.send_head()
@@ -46,7 +46,7 @@ class TestSecureHandler(BaseHTTPServer.BaseHTTPRequestHandler):
         try:
             handler = self.marshall_handler()
             handler.do_request(self)
-        except:
+        except:  # noqa: E722
             self.send_response(401)
             self.send_header("WWW-Authenticate", "Basic realm='Secure Area'")
             self.send_header("Content-type", "text/plain")
@@ -97,12 +97,12 @@ class CompatTCPServer(SocketServer.TCPServer):
         if self.isPy25:
             try:
                 request, client_address = self.get_request()
-            except socket.error:
+            except socket.error:  # noqa: F821
                 return
             if self.verify_request(request, client_address):
                 try:
                     self.process_request(request, client_address)
-                except:
+                except:  # noqa: E722
                     self.handle_error(request, client_address)
                     self.close_request(request)
 
@@ -138,7 +138,7 @@ class TestServer(threading.Thread):
             try:
                 self.httpd = CompatTCPServer(('', self._port), handle)
                 break
-            except:
+            except:  # noqa: E722
                 self._port = self._port + random.randint(1, 100)
 
     def port(self):
@@ -150,4 +150,3 @@ class TestServer(threading.Thread):
     def shutdown(self):
         self.httpd.shutdown()
         self.join()
-
index e471cf16779e89351926863a46f8bd1f02351943..90540558501cce41f359837631049e70ebfdf058 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import time
@@ -14,7 +15,7 @@ class TimeoutException(Exception):
         return repr(self.msg)
 
 
-class ProcessTimer(object):
+class ProcessTimer:
     '''Timeout function for controlling a subprocess.Popen instance.
 
     Naive implementation using busy loop, see later other means
index cee6aa14d2ec72fcfb6dcb34dc0727ae0cf4d383..417db8bec0702466c663e92a7f817bfc97f17866 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 'Tests for processtimer.py'
 
index a54b451b338671e16f96fa9891b05e055f666b3e..129d689894e555d3e9af8d36c5f466890ca9a33b 100644 (file)
    Memcheck:Value4
    fun:SHA1_Update
 }
-
-
index 7a532df213f67620458ef6bf184284509405a51b..8c7d76c02c76c58a9856115e77de1e6ac71b7981 100644 (file)
@@ -1,5 +1,5 @@
 set(shiboken_MAJOR_VERSION "6")
-set(shiboken_MINOR_VERSION "7")
-set(shiboken_MICRO_VERSION "2")
+set(shiboken_MINOR_VERSION "8")
+set(shiboken_MICRO_VERSION "2.1")
 set(shiboken_PRE_RELEASE_VERSION_TYPE "")
 set(shiboken_PRE_RELEASE_VERSION "")
index 4bb16b2dd0772caf6110d56404f7d2e0697ca11f..635778b840d8ed2a33a1a7e6273dc938c5e570a6 100644 (file)
@@ -9,4 +9,3 @@ Lauro Moura <lauro.neto@openbossa.org>
 Luciano Wolf <luciano.wolf@openbossa.org>
 Marcelo Lira <marcelo.lira@openbossa.org>
 Renato Araujo Oliveira Filho <renato.filho@openbossa.org>
-
index 6e802fb53bd1248244c4a174238695225d7ff501..6484b68a476307e3351605d03bedac80d4719bbb 100644 (file)
@@ -5,4 +5,3 @@ Lauro Moura <lauro.neto@openbossa.org>
 Luciano Wolf <luciano.wolf@openbossa.org>
 Marcelo Lira <marcelo.lira@openbossa.org>
 Renato Araujo Oliveira Filho <renato.filho@openbossa.org>
-
index 7aa2fbd11b467dbcda55ae9c27ef19e707813b06..37758f7f474e8fd6a224367bf9006308b427b92e 100644 (file)
@@ -35,6 +35,7 @@ customtypenentry.h
 debughelpers_p.h
 dependency.h
 documentation.cpp documentation.h
+documentation_enums.h
 dotview.cpp dotview.h
 enclosingclassmixin.cpp enclosingclassmixin.h
 enumtypeentry.h
index 0720fca7eb97926fc3c642a8c434f2ad58c05c7b..b1b6fa76455518d9e966b5f1c637283adcbe1da5 100644 (file)
@@ -81,7 +81,7 @@ QTextStream &operator<<(QTextStream &str, const RejectEntry &re)
     return str;
 }
 
-static void applyCachedFunctionModifications(AbstractMetaFunction *metaFunction,
+static void applyCachedFunctionModifications(const AbstractMetaFunctionPtr &metaFunction,
                                              const FunctionModificationList &functionMods)
 {
     for (const FunctionModification &mod : functionMods) {
@@ -317,7 +317,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelIte
         return;
     }
 
-    AbstractMetaFunction *metaFunction = traverseFunction(item, baseoperandClass);
+    auto metaFunction = traverseFunction(item, baseoperandClass);
     if (metaFunction == nullptr)
         return;
 
@@ -350,7 +350,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelIte
     }
     metaFunction->setFlags(flags);
     metaFunction->setAccess(Access::Public);
-    AbstractMetaClass::addFunction(baseoperandClass, AbstractMetaFunctionCPtr(metaFunction));
+    AbstractMetaClass::addFunction(baseoperandClass, metaFunction);
     if (!metaFunction->arguments().isEmpty()) {
         const auto include = metaFunction->arguments().constFirst().type().typeEntry()->include();
         baseoperandClass->typeEntry()->addArgumentInclude(include);
@@ -371,7 +371,7 @@ bool AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem
    if (streamedClass == nullptr)
        return false;
 
-   AbstractMetaFunction *streamFunction = traverseFunction(item, streamedClass);
+   auto streamFunction = traverseFunction(item, streamedClass);
    if (!streamFunction)
        return false;
 
@@ -401,7 +401,7 @@ bool AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem
         funcClass = streamClass;
     }
 
-    AbstractMetaClass::addFunction(funcClass, AbstractMetaFunctionCPtr(streamFunction));
+    AbstractMetaClass::addFunction(funcClass, streamFunction);
     auto funcTe = funcClass->typeEntry();
     if (funcClass == streamClass)
         funcTe->addArgumentInclude(streamedClass->typeEntry()->include());
@@ -461,7 +461,7 @@ FileModelItem AbstractMetaBuilderPrivate::buildDom(QByteArrayList arguments,
                           + clang::languageLevelOption(level));
     }
     FileModelItem result = clang::parse(arguments, addCompilerSupportArguments,
-                                        clangFlags, builder)
+                                        level, clangFlags, builder)
         ? builder.dom() : FileModelItem();
     const clang::BaseVisitor::Diagnostics &diagnostics = builder.diagnostics();
     if (const auto diagnosticsCount = diagnostics.size()) {
@@ -555,17 +555,16 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom,
         if (!funcEntry || !funcEntry->generateCode())
             continue;
 
-        AbstractMetaFunction *metaFunc = traverseFunction(func, nullptr);
-        if (!metaFunc)
+        auto metaFuncPtr = traverseFunction(func, nullptr);
+        if (!metaFuncPtr)
             continue;
 
-        AbstractMetaFunctionCPtr metaFuncPtr(metaFunc);
-        if (!funcEntry->hasSignature(metaFunc->minimalSignature()))
+        if (!funcEntry->hasSignature(metaFuncPtr->minimalSignature()))
             continue;
 
-        metaFunc->setTypeEntry(funcEntry);
-        applyFunctionModifications(metaFunc);
-        metaFunc->applyTypeModifications();
+        metaFuncPtr->setTypeEntry(funcEntry);
+        applyFunctionModifications(metaFuncPtr);
+        metaFuncPtr->applyTypeModifications();
 
         setInclude(funcEntry, func->fileName());
 
@@ -1348,7 +1347,7 @@ void AbstractMetaBuilderPrivate::traverseFields(const ScopeModelItem &scope_item
     }
 }
 
-void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction)
+void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(const AbstractMetaFunctionPtr &metaFunction)
 {
     if (!metaFunction->isConversionOperator())
         return;
@@ -1372,13 +1371,13 @@ void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaF
     metaFunction->setType(metaType);
 }
 
-AbstractMetaFunctionRawPtrList
+AbstractMetaFunctionList
     AbstractMetaBuilderPrivate::classFunctionList(const ScopeModelItem &scopeItem,
                                                   AbstractMetaClass::Attributes *constructorAttributes,
                                                   const AbstractMetaClassPtr &currentClass)
 {
     *constructorAttributes = {};
-    AbstractMetaFunctionRawPtrList result;
+    AbstractMetaFunctionList result;
     const FunctionList &scopeFunctionList = scopeItem->functions();
     result.reserve(scopeFunctionList.size());
     const bool isNamespace = currentClass->isNamespace();
@@ -1388,7 +1387,7 @@ AbstractMetaFunctionRawPtrList
         } else if (function->isSpaceshipOperator() && !function->isDeleted()) {
             if (currentClass)
                 AbstractMetaClass::addSynthesizedComparisonOperators(currentClass);
-        } else if (auto *metaFunction = traverseFunction(function, currentClass)) {
+        } else if (auto metaFunction = traverseFunction(function, currentClass)) {
             result.append(metaFunction);
         } else if (!function->isDeleted() && function->functionType() == CodeModel::Constructor) {
             auto arguments = function->arguments();
@@ -1404,11 +1403,11 @@ void AbstractMetaBuilderPrivate::traverseFunctions(const ScopeModelItem& scopeIt
                                                    const AbstractMetaClassPtr &metaClass)
 {
     AbstractMetaClass::Attributes constructorAttributes;
-    const AbstractMetaFunctionRawPtrList functions =
+    const AbstractMetaFunctionList functions =
         classFunctionList(scopeItem, &constructorAttributes, metaClass);
     metaClass->setAttributes(metaClass->attributes() | constructorAttributes);
 
-    for (AbstractMetaFunction *metaFunction : functions) {
+    for (const auto &metaFunction : functions) {
         if (metaClass->isNamespace())
             metaFunction->setCppAttribute(FunctionAttribute::Static);
 
@@ -1461,23 +1460,20 @@ void AbstractMetaBuilderPrivate::traverseFunctions(const ScopeModelItem& scopeIt
         if (!metaFunction->isDestructor()
             && !(metaFunction->isPrivate() && metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction)) {
 
-            if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction))
-                qCWarning(lcShiboken, "%s", qPrintable(msgSignalOverloaded(metaClass, metaFunction)));
+            if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction.get()))
+                qCWarning(lcShiboken, "%s", qPrintable(msgSignalOverloaded(metaClass,
+                                                                           metaFunction.get())));
 
             if (metaFunction->isConversionOperator())
                 fixReturnTypeOfConversionOperator(metaFunction);
 
-            AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction));
+            AbstractMetaClass::addFunction(metaClass, metaFunction);
             applyFunctionModifications(metaFunction);
         } else if (metaFunction->isDestructor()) {
             metaClass->setHasPrivateDestructor(metaFunction->isPrivate());
             metaClass->setHasProtectedDestructor(metaFunction->isProtected());
             metaClass->setHasVirtualDestructor(metaFunction->isVirtual());
         }
-        if (!metaFunction->ownerClass()) {
-            delete metaFunction;
-            metaFunction = nullptr;
-        }
     }
 
     fillAddedFunctions(metaClass);
@@ -1539,7 +1535,7 @@ QStringList AbstractMetaBuilder::definitionNames(const QString &name,
     return result;
 }
 
-void AbstractMetaBuilderPrivate::applyFunctionModifications(AbstractMetaFunction *func)
+void AbstractMetaBuilderPrivate::applyFunctionModifications(const AbstractMetaFunctionPtr &func)
 {
     AbstractMetaFunction& funcRef = *func;
     for (const FunctionModification &mod : func->modifications(func->implementingClass())) {
@@ -1677,15 +1673,14 @@ static AbstractMetaFunction::FunctionType functionTypeFromName(const QString &);
 bool AbstractMetaBuilderPrivate::traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc,
                                                              QString *errorMessage)
 {
-    AbstractMetaFunction *metaFunction =
-        traverseAddedFunctionHelper(addedFunc, nullptr, errorMessage);
+    auto metaFunction = traverseAddedFunctionHelper(addedFunc, nullptr, errorMessage);
     if (metaFunction == nullptr)
         return false;
-    m_globalFunctions << AbstractMetaFunctionCPtr(metaFunction);
+    m_globalFunctions << metaFunction;
     return true;
 }
 
-AbstractMetaFunction *
+AbstractMetaFunctionPtr
     AbstractMetaBuilderPrivate::traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
                                                             const AbstractMetaClassPtr &metaClass /* = {} */,
                                                             QString *errorMessage)
@@ -1696,10 +1691,10 @@ AbstractMetaFunction *
             msgAddedFunctionInvalidReturnType(addedFunc->name(),
                                               addedFunc->returnType().qualifiedName(),
                                               *errorMessage, metaClass);
-        return nullptr;
+        return {};
     }
 
-    auto *metaFunction = new AbstractMetaFunction(addedFunc);
+    auto metaFunction = std::make_shared<AbstractMetaFunction>(addedFunc);
     metaFunction->setType(returnType.value());
     metaFunction->setFunctionType(functionTypeFromName(addedFunc->name()));
 
@@ -1717,8 +1712,7 @@ AbstractMetaFunction *
                 msgAddedFunctionInvalidArgType(addedFunc->name(),
                                                arg.typeInfo.qualifiedName(), i + 1,
                                                *errorMessage, metaClass);
-            delete metaFunction;
-            return nullptr;
+            return {};
         }
         type->decideUsagePattern();
 
@@ -1775,8 +1769,7 @@ bool AbstractMetaBuilderPrivate::traverseAddedMemberFunction(const AddedFunction
                                                              const AbstractMetaClassPtr &metaClass,
                                                              QString *errorMessage)
 {
-    AbstractMetaFunction *metaFunction =
-        traverseAddedFunctionHelper(addedFunc, metaClass, errorMessage);
+    auto metaFunction = traverseAddedFunctionHelper(addedFunc, metaClass, errorMessage);
     if (metaFunction == nullptr)
         return false;
 
@@ -1796,12 +1789,13 @@ bool AbstractMetaBuilderPrivate::traverseAddedMemberFunction(const AddedFunction
 
     metaFunction->setDeclaringClass(metaClass);
     metaFunction->setImplementingClass(metaClass);
-    AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction));
+    AbstractMetaClass::addFunction(metaClass, metaFunction);
     metaClass->setHasNonPrivateConstructor(true);
     return true;
 }
 
-void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods)
+void AbstractMetaBuilderPrivate::fixArgumentNames(const AbstractMetaFunctionPtr &func,
+                                                  const FunctionModificationList &mods)
 {
     AbstractMetaArgumentList &arguments = func->arguments();
 
@@ -1920,7 +1914,7 @@ static AbstractMetaFunction::FunctionType functionTypeFromName(const QString &na
 
 // Apply the <array> modifications of the arguments
 static bool applyArrayArgumentModifications(const FunctionModificationList &functionMods,
-                                            AbstractMetaFunction *func,
+                                            const AbstractMetaFunctionPtr &func,
                                             QString *errorMessage)
 {
     for (const FunctionModification &mod : functionMods) {
@@ -1981,13 +1975,37 @@ void AbstractMetaBuilderPrivate::rejectFunction(const FunctionModelItem &functio
     m_rejectedFunctions.insert({reason, signatureWithType, sortKey, rejectReason});
 }
 
-AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem,
-                                                                   const AbstractMetaClassPtr &currentClass)
+// Check for special Qt argument types which should be ignored.
+enum class QtSpecialArgument
+{
+    None,
+    PrivateSignal,
+    Disambiguated // by QT6_DECL_NEW_OVERLOAD_TAIL
+};
+
+static QtSpecialArgument qtSpecialArgument(CodeModel::FunctionType functionType,
+                                           const ArgumentList &arguments)
+{
+    if (arguments.isEmpty())
+        return QtSpecialArgument::None;
+    const QStringList &lastArgumentType = arguments.constLast()->type().qualifiedName();
+    if (functionType == CodeModel::Signal && lastArgumentType.constLast() == "QPrivateSignal"_L1)
+        return QtSpecialArgument::PrivateSignal;
+    if (lastArgumentType.size() == 2 && lastArgumentType.constFirst() == "Qt"_L1
+        && lastArgumentType.constLast() == "Disambiguated_t"_L1) {
+        return QtSpecialArgument::Disambiguated;
+    }
+    return QtSpecialArgument::None;
+}
+
+AbstractMetaFunctionPtr
+    AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem,
+                                                 const AbstractMetaClassPtr &currentClass)
 {
     const auto *tdb = TypeDatabase::instance();
 
     if (!functionItem->templateParameters().isEmpty())
-        return nullptr;
+        return {};
 
     if (functionItem->isDeleted()) {
         switch (functionItem->functionType()) {
@@ -2001,7 +2019,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
         default:
             break;
         }
-        return nullptr;
+        return {};
     }
     const QString &functionName = functionItem->name();
     const QString className = currentClass != nullptr ?
@@ -2011,7 +2029,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
         // Skip enum helpers generated by Q_ENUM
         if ((currentClass == nullptr || currentClass->isNamespace())
             && (functionName == u"qt_getEnumMetaObject" || functionName == u"qt_getEnumName")) {
-                return nullptr;
+                return {};
             }
 
         // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT
@@ -2019,10 +2037,10 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
         if (currentClass != nullptr) {
             if (functionName == u"qt_check_for_QGADGET_macro"
                 || functionName.startsWith(u"qt_meta")) {
-                return nullptr;
+                return {};
             }
             if (functionName == u"metaObject" && className != u"QObject")
-                return nullptr;
+                return {};
         }
     } // PySide extensions
 
@@ -2030,7 +2048,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
     if (tdb->isFunctionRejected(className, functionName, &rejectReason)) {
         rejectFunction(functionItem, currentClass,
                        AbstractMetaBuilder::GenerationDisabled, rejectReason);
-        return nullptr;
+        return {};
     }
 
     const QString &signature = functionSignature(functionItem);
@@ -2041,22 +2059,22 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
             qCInfo(lcShiboken, "%s::%s was rejected by the type database (%s).",
                    qPrintable(className), qPrintable(signature), qPrintable(rejectReason));
         }
-        return nullptr;
+        return {};
     }
 
     if (functionItem->isFriend())
-        return nullptr;
+        return {};
 
     const auto cppAttributes = functionItem->attributes();
     const bool deprecated = cppAttributes.testFlag(FunctionAttribute::Deprecated);
     if (deprecated && m_skipDeprecated) {
         rejectFunction(functionItem, currentClass,
                        AbstractMetaBuilder::GenerationDisabled, u" is deprecated."_s);
-        return nullptr;
+        return {};
     }
 
     AbstractMetaFunction::Flags flags;
-    auto *metaFunction = new AbstractMetaFunction(functionName);
+    auto metaFunction = std::make_shared<AbstractMetaFunction>(functionName);
     metaFunction->setCppAttributes(cppAttributes);
     const QByteArray cSignature = signature.toUtf8();
     const QString unresolvedSignature =
@@ -2089,8 +2107,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
         if (tdb->isReturnTypeRejected(className, returnType.toString(), &rejectReason)) {
             rejectFunction(functionItem, currentClass,
                            AbstractMetaBuilder::GenerationDisabled, rejectReason);
-            delete metaFunction;
-            return nullptr;
+            return {};
         }
 
         TranslateTypeFlags flags;
@@ -2104,8 +2121,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
                       qPrintable(msgSkippingFunction(functionItem, signature, reason)));
             rejectFunction(functionItem, currentClass,
                            AbstractMetaBuilder::UnmatchedReturnType, reason);
-            delete metaFunction;
-            return nullptr;
+            return {};
         }
 
         metaFunction->setType(type.value());
@@ -2114,13 +2130,23 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
     }
 
     ArgumentList arguments = functionItem->arguments();
-    // Add private signals for documentation purposes
-    if (!arguments.isEmpty()
-        && m_apiExtractorFlags.testFlag(ApiExtractorFlag::UsePySideExtensions)
-        && functionItem->functionType() == CodeModel::Signal
-        && arguments.constLast()->type().qualifiedName().constLast() == u"QPrivateSignal") {
-        flags.setFlag(AbstractMetaFunction::Flag::PrivateSignal);
-        arguments.removeLast();
+    if (m_apiExtractorFlags.testFlag(ApiExtractorFlag::UsePySideExtensions)) {
+        switch (qtSpecialArgument(functionItem->functionType(), arguments)) {
+        case QtSpecialArgument::None:
+            break;
+        case QtSpecialArgument::PrivateSignal:
+            flags.setFlag(AbstractMetaFunction::Flag::PrivateSignal);
+            arguments.removeLast(); // Add private signals for documentation purposes
+            break;
+        case QtSpecialArgument::Disambiguated:
+            arguments.removeLast(); // Strip QT6_DECL_NEW_OVERLOAD_TAIL
+            if (!currentClass || currentClass->typeEntry()->generateCode()) {
+                const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
+                qCWarning(lcShiboken, "%s",
+                          qPrintable(msgStrippingQtDisambiguatedArgument(functionItem, signature)));
+            }
+            break;
+        }
     }
 
     if (arguments.size() == 1) {
@@ -2136,8 +2162,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
         if (tdb->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) {
             rejectFunction(functionItem, currentClass,
                            AbstractMetaBuilder::GenerationDisabled, rejectReason);
-            delete metaFunction;
-            return nullptr;
+            return {};
         }
 
         TranslateTypeFlags flags;
@@ -2164,8 +2189,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
                       qPrintable(msgSkippingFunction(functionItem, signature, reason)));
             rejectFunction(functionItem, currentClass,
                            AbstractMetaBuilder::UnmatchedArgumentType, reason);
-            delete metaFunction;
-            return nullptr;
+            return {};
         }
 
         auto metaType = metaTypeO.value();
@@ -2188,8 +2212,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
     AbstractMetaArgumentList &metaArguments = metaFunction->arguments();
 
     const FunctionModificationList functionMods = currentClass
-        ? AbstractMetaFunction::findClassModifications(metaFunction, currentClass)
-        : AbstractMetaFunction::findGlobalModifications(metaFunction);
+        ? AbstractMetaFunction::findClassModifications(metaFunction.get(), currentClass)
+        : AbstractMetaFunction::findGlobalModifications(metaFunction.get());
 
     applyCachedFunctionModifications(metaFunction, functionMods);
 
@@ -2214,7 +2238,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
             && metaFunction->argumentName(i + 1, false, currentClass).isEmpty()) {
             qCWarning(lcShiboken, "%s",
                       qPrintable(msgUnnamedArgumentDefaultExpression(currentClass, i + 1,
-                                                                     className, metaFunction)));
+                                                                     className, metaFunction.get())));
         }
 
     }
@@ -3315,7 +3339,7 @@ AbstractMetaFunctionPtr
     }
 
     QString errorMessage;
-    if (!applyArrayArgumentModifications(f->modifications(subclass), f.get(),
+    if (!applyArrayArgumentModifications(f->modifications(subclass), f,
                                          &errorMessage)) {
         qCWarning(lcShiboken, "While specializing %s (%s): %s",
                   qPrintable(subclass->name()), qPrintable(templateClass->name()),
@@ -3408,7 +3432,7 @@ void AbstractMetaBuilderPrivate::parseQ_Properties(const AbstractMetaClassPtr &m
         if (spec.has_value()) {
             spec->setIndex(i);
             metaClass->addPropertySpec(spec.value());
-        } else {
+        } else if (!errorMessage.isEmpty()) {
             QString message;
             QTextStream str(&message);
             str << metaClass->sourceLocation() << errorMessage;
@@ -3519,7 +3543,7 @@ static QList<std::shared_ptr<MetaClass> >
     topologicalSortHelper(const QList<std::shared_ptr<MetaClass> > &classList,
                           const Dependencies &additionalDependencies)
 {
-    Graph<std::shared_ptr<MetaClass> > graph(classList.cbegin(), classList.cend());
+    Graph<std::shared_ptr<MetaClass> > graph(classList);
 
     for (const auto &dep : additionalDependencies) {
         if (!graph.addEdge(dep.parent, dep.child)) {
index 68eef737a550b4be5028ccfa8909e9dd025c2f12..986e87341be48de43fb8b247b6fe736e694241ef 100644 (file)
@@ -67,7 +67,7 @@ bool AbstractMetaBuilderPrivate::isQualifiedCppIdentifier(QStringView e)
 
 static bool isIntegerConstant(const QStringView expr)
 {
-    bool isNumber;
+    bool isNumber{};
     auto n = expr.toInt(&isNumber, /* guess base: 0x or decimal */ 0);
     Q_UNUSED(n);
     return isNumber;
@@ -75,7 +75,7 @@ static bool isIntegerConstant(const QStringView expr)
 
 static bool isFloatConstant(const QStringView expr)
 {
-    bool isNumber;
+    bool isNumber{};
     auto d = expr.toDouble(&isNumber);
     Q_UNUSED(d);
     return isNumber;
@@ -92,7 +92,7 @@ QString AbstractMetaBuilderPrivate::fixEnumDefault(const AbstractMetaType &type,
     if (isIntegerConstant(expr))
         return expr;
 
-    const QString field = qualifyStaticField(klass, expr);
+    QString field = qualifyStaticField(klass, expr);
     if (!field.isEmpty())
         return field;
 
@@ -164,7 +164,7 @@ QString AbstractMetaBuilderPrivate::fixEnumDefault(const AbstractMetaType &type,
             qualified.prepend(resolveEnumValueScopePrefix(metaEnum, token));
         qualifiedTokens.append(qualified);
     }
-    const QString qualifiedExpression = qualifiedTokens.join(u" | "_s);
+    QString qualifiedExpression = qualifiedTokens.join(u" | "_s);
     if (!typeCast)
         return qualifiedExpression;
 
index e65a4f176d601e57dbd8bd842cd557667e665769..4e337339eb29b2694beb14846878ccabb99b875a 100644 (file)
@@ -98,20 +98,21 @@ public:
                                                  const QSet<QString> &enumsDeclarations);
     void traverseEnums(const ScopeModelItem &item, const AbstractMetaClassPtr &parent,
                        const QStringList &enumsDeclarations);
-    AbstractMetaFunctionRawPtrList classFunctionList(const ScopeModelItem &scopeItem,
-                                                     AbstractMetaClass::Attributes *constructorAttributes,
-                                                     const AbstractMetaClassPtr &currentClass);
+    AbstractMetaFunctionList classFunctionList(const ScopeModelItem &scopeItem,
+                                               AbstractMetaClass::Attributes *constructorAttributes,
+                                               const AbstractMetaClassPtr &currentClass);
     void traverseFunctions(const ScopeModelItem& item,
                            const AbstractMetaClassPtr &parent);
-    static void applyFunctionModifications(AbstractMetaFunction *func);
+    static void applyFunctionModifications(const AbstractMetaFunctionPtr &func);
     void traverseFields(const ScopeModelItem &item, const AbstractMetaClassPtr &parent);
     bool traverseStreamOperator(const FunctionModelItem &functionItem,
                                 const AbstractMetaClassPtr &currentClass);
     void traverseOperatorFunction(const FunctionModelItem &item,
                                   const AbstractMetaClassPtr &currentClass);
-    AbstractMetaFunction *traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
-                                                      const AbstractMetaClassPtr &metaClass,
-                                                      QString *errorMessage);
+    AbstractMetaFunctionPtr
+        traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
+                                    const AbstractMetaClassPtr &metaClass,
+                                    QString *errorMessage);
     bool traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc,
                                      QString *errorMessage);
     bool traverseAddedMemberFunction(const AddedFunctionPtr &addedFunc,
@@ -121,8 +122,9 @@ public:
                         const AbstractMetaClassPtr &currentClass,
                         AbstractMetaBuilder::RejectReason reason,
                         const QString &rejectReason);
-        AbstractMetaFunction *traverseFunction(const FunctionModelItem &function,
-                                           const AbstractMetaClassPtr &currentClass);
+    AbstractMetaFunctionPtr
+        traverseFunction(const FunctionModelItem &function,
+                         const AbstractMetaClassPtr &currentClass);
     std::optional<AbstractMetaField> traverseField(const VariableModelItem &field,
                                                    const AbstractMetaClassCPtr &cls);
     void checkFunctionModifications() const;
@@ -142,7 +144,7 @@ public:
      *   said class.
      *   \param metaFunction conversion operator function to be fixed.
      */
-    static void fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction);
+    static void fixReturnTypeOfConversionOperator(const AbstractMetaFunctionPtr &metaFunction);
 
     void parseQ_Properties(const AbstractMetaClassPtr &metaClass,
                            const QStringList &declarations);
@@ -212,7 +214,8 @@ public:
 
     void sortLists();
     void setInclude(const TypeEntryPtr &te, const QString &path) const;
-    static void fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods);
+    static void fixArgumentNames(const AbstractMetaFunctionPtr &func,
+                                 const FunctionModificationList &mods);
 
     void fillAddedFunctions(const AbstractMetaClassPtr &metaClass);
     AbstractMetaClassCPtr resolveTypeSystemTypeDef(const AbstractMetaType &t) const;
index 780170c2291c72826e9ef0604b8fae86162ae83e..8374be1702da8dadb66934385d5227e658b4eda6 100644 (file)
@@ -195,7 +195,7 @@ std::optional<AbstractMetaEnumValue>
 {
     if (isAnonymous())
         return findMatchingEnumValue(d->m_enumValues, value);
-    const int sepPos = value.indexOf(u"::");
+    const auto sepPos = value.indexOf(u"::");
     if (sepPos == -1)
         return findMatchingEnumValue(d->m_enumValues, value);
     if (name() == value.left(sepPos))
index 27a76d04d4f2a852482b0329f0e5d49b5eb841a6..b4385228c526e9738ebf92885dc2cc733ba36a4e 100644 (file)
@@ -15,6 +15,8 @@
 
 #include <QtCore/QDebug>
 
+#include <algorithm>
+
 using namespace Qt::StringLiterals;
 
 class AbstractMetaFieldData : public QSharedData
@@ -56,25 +58,20 @@ std::optional<AbstractMetaField>
 /*******************************************************************************
  * Indicates that this field has a modification that removes it
  */
+static bool isRemoved(const FieldModification &mod) { return mod.isRemoved(); }
+
 bool AbstractMetaField::isModifiedRemoved() const
 {
     const FieldModificationList &mods = modifications();
-    for (const FieldModification &mod : mods) {
-        if (mod.isRemoved())
-            return true;
-    }
-
-    return false;
+    return std::any_of(mods.cbegin(), mods.cend(), isRemoved);
 }
 
+static bool isOpaqueContainer(const FieldModification &mod) { return mod.isOpaqueContainer(); }
+
 bool AbstractMetaField::generateOpaqueContainer() const
 {
     const FieldModificationList &mods = modifications();
-    for (const FieldModification &mod : mods) {
-        if (mod.isOpaqueContainer())
-            return true;
-    }
-    return false;
+    return std::any_of(mods.cbegin(), mods.cend(), isOpaqueContainer);
 }
 
 const AbstractMetaType &AbstractMetaField::type() const
index 11a02f154f4d4d8b80c55d81be652afd3add0644..5198d69a472be5a2baebbbd73e67cf6478e80a49 100644 (file)
@@ -289,27 +289,28 @@ void AbstractMetaFunction::setFlags(Flags f)
 /*******************************************************************************
  * Indicates that this function has a modification that removes it
  */
+
+static bool fmIsRemoved(const FunctionModification &mod) { return mod.isRemoved(); }
+
 bool AbstractMetaFunction::isModifiedRemoved(AbstractMetaClassCPtr cls) const
 {
     if (!isInGlobalScope() && !cls)
         cls = d->m_implementingClass;
-    for (const auto &mod : modifications(cls)) {
-        if (mod.isRemoved())
-            return true;
-    }
+    const auto &mods = modifications(cls);
+    return std::any_of(mods.cbegin(), mods.cend(), fmIsRemoved);
+}
 
-    return false;
+static bool isFinal(const FunctionModification &mod)
+{
+    return mod.modifiers().testFlag(FunctionModification::Final);
 }
 
 bool AbstractMetaFunction::isModifiedFinal(AbstractMetaClassCPtr cls) const
 {
     if (!isInGlobalScope() && cls == nullptr)
         cls = d->m_implementingClass;
-    for (const auto &mod : modifications(cls)) {
-        if (mod.modifiers().testFlag(FunctionModification::Final))
-            return true;
-    }
-    return false;
+    const auto &mods = modifications(cls);
+    return std::any_of(mods.cbegin(), mods.cend(), isFinal);
 }
 
 bool AbstractMetaFunction::isVoid() const
@@ -423,7 +424,7 @@ bool AbstractMetaFunction::isConstOverloadOf(const AbstractMetaFunction *other)
     }
 
     // Match "const Foo &getFoo() const" / "Foo &getFoo()" / "Foo getFoo() const"
-    const auto otherType = other->type();
+    const auto &otherType = other->type();
     if (d->m_type.name() != otherType.name()
         || d->m_type.indirectionsV() != otherType.indirectionsV()) {
         return false;
@@ -465,17 +466,17 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const
     return cpy;
 }
 
+static bool usesRValueReference(const AbstractMetaArgument &a)
+{
+    return a.type().referenceType() == RValueReference;
+}
+
 bool AbstractMetaFunction::usesRValueReferences() const
 {
-    if (d->m_functionType == MoveConstructorFunction || d->m_functionType == MoveAssignmentOperatorFunction)
-        return true;
-    if (d->m_type.referenceType() == RValueReference)
-        return true;
-    for (const AbstractMetaArgument &a : d->m_arguments) {
-        if (a.type().referenceType() == RValueReference)
-            return true;
-    }
-    return false;
+    return d->m_functionType == MoveConstructorFunction
+        || d->m_functionType == MoveAssignmentOperatorFunction
+        || d->m_type.referenceType() == RValueReference
+        || std::any_of(d->m_arguments.cbegin(), d->m_arguments.cend(), usesRValueReference);
 }
 
 bool AbstractMetaFunction::generateBinding() const
@@ -542,7 +543,8 @@ QString AbstractMetaFunctionPrivate::signature() const
                 m_cachedSignature += u", "_s;
             m_cachedSignature += t.cppSignature();
             // We need to have the argument names in the qdoc files
-            m_cachedSignature += u' ';
+            if (!m_cachedSignature.endsWith(u'*') && !m_cachedSignature.endsWith(u'&'))
+                m_cachedSignature += u' ';
             m_cachedSignature += a.name();
         }
         m_cachedSignature += u')';
@@ -653,7 +655,7 @@ ArgumentOwner AbstractMetaFunction::argumentOwner(const AbstractMetaClassCPtr &c
             return argumentMod.owner();
         }
     }
-    return ArgumentOwner();
+    return {};
 }
 
 QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const
@@ -670,7 +672,7 @@ QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int
         }
     }
 
-    return QString();
+    return {};
 }
 
 bool AbstractMetaFunction::hasConversionRule(TypeSystem::Language language, int idx) const
@@ -734,6 +736,11 @@ void AbstractMetaFunction::setArguments(const AbstractMetaArgumentList &argument
     d->m_arguments = arguments;
 }
 
+void AbstractMetaFunction::setArgumentName(qsizetype a, const QString &name)
+{
+    d->m_arguments[a].setName(name);
+}
+
 void AbstractMetaFunction::addArgument(const AbstractMetaArgument &argument)
 {
     d->m_arguments << argument;
@@ -962,7 +969,8 @@ void AbstractMetaFunction::applyTypeModifications()
                 && !d->applyTypeModification(this, am.modifiedType(),
                                              n, &errorMessage)) {
                 throw Exception(errorMessage);
-            } else if (am.isRemoved() && n != 0) {
+            }
+            if (am.isRemoved() && n != 0) {
                 if (n < 1 || n > d->m_arguments.size()) {
                     errorMessage =
                         msgArgumentRemovalFailed(this, n,
@@ -1112,7 +1120,7 @@ void AbstractMetaFunction::clearModificationsCache()
     d->m_modificationCache.clear();
 }
 
-const DocModificationList AbstractMetaFunction::addedFunctionDocModifications() const
+DocModificationList AbstractMetaFunction::addedFunctionDocModifications() const
 {
     return d->m_addedFunction
         ? d->m_addedFunction->docModifications() : DocModificationList{};
@@ -1120,7 +1128,7 @@ const DocModificationList AbstractMetaFunction::addedFunctionDocModifications()
 
 QString AbstractMetaFunction::argumentName(int index,
                                            bool /* create */,
-                                           AbstractMetaClassCPtr  /* implementor */) const
+                                           const AbstractMetaClassCPtr & /* implementor */) const
 {
     return d->m_arguments[--index].name();
 }
@@ -1161,14 +1169,15 @@ bool AbstractMetaFunction::isCallOperator() const
     return d->m_name == u"operator()";
 }
 
+static bool isCodeInjection(const FunctionModification &mod)
+{
+    return mod.isCodeInjection();
+}
+
 bool AbstractMetaFunction::hasInjectedCode() const
 {
     const FunctionModificationList &mods = modifications(ownerClass());
-    for (const FunctionModification &mod : mods) {
-        if (mod.isCodeInjection())
-            return true;
-    }
-    return false;
+    return std::any_of(mods.cbegin(), mods.cend(), isCodeInjection);
 }
 
 // Traverse the code snippets, return true if predicate returns true
@@ -1586,13 +1595,15 @@ bool AbstractMetaFunction::injectedCodeUsesArgument(int argumentIndex) const
                              }, TypeSystem::CodeSnipPositionAny);
 }
 
+static bool isModifiedToPrivate(const FunctionModification &mod)
+{
+    return mod.modifiers().testFlag(FunctionModification::Private);
+}
+
 bool AbstractMetaFunction::isVisibilityModifiedToPrivate() const
 {
-    for (const auto &mod : modifications()) {
-        if (mod.modifiers().testFlag(FunctionModification::Private))
-            return true;
-    }
-    return false;
+    const auto &mods = modifications();
+    return std::any_of(mods.cbegin(), mods.cend(), isModifiedToPrivate);
 }
 
 struct ComparisonOperator
index e252e439dcf67273dd2a7dd4f9d1ca4c6bcced8d..3bc38ab2a7ca00696aee579a540790f283528ddf 100644 (file)
@@ -260,6 +260,7 @@ public:
     const AbstractMetaArgumentList &arguments() const;
     AbstractMetaArgumentList &arguments();
     void setArguments(const AbstractMetaArgumentList &arguments);
+    void setArgumentName(qsizetype a, const QString &name); // For documentation purposes
     void addArgument(const AbstractMetaArgument &argument);
     int actualMinimumArgumentCount() const;
     // Return the argument index accounting for the isModifiedRemoved arguments [0..n-1]
@@ -363,7 +364,7 @@ public:
     const FunctionModificationList &modifications(AbstractMetaClassCPtr implementor = {}) const;
     void clearModificationsCache();
 
-    const DocModificationList addedFunctionDocModifications() const;
+    DocModificationList addedFunctionDocModifications() const;
 
     static FunctionModificationList findClassModifications(const AbstractMetaFunction *f,
                                                            AbstractMetaClassCPtr implementor);
@@ -372,7 +373,8 @@ public:
     /**
      * Return the argument name if there is a modification the renamed value will be returned
      */
-    QString argumentName(int index, bool create = true, AbstractMetaClassCPtr cl = {}) const;
+    QString argumentName(int index, bool create = true,
+                         const AbstractMetaClassCPtr &cl = {}) const;
 
     int propertySpecIndex() const;
     void setPropertySpecIndex(int i);
@@ -474,7 +476,7 @@ inline bool AbstractMetaFunction::isPropertyResetter() const
     return attributes().testFlag(PropertyResetter);
 }
 
-Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult)
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult);
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::Attributes);
 
index fb49cc9d0824bdfbd6db9215908d5872d7bcb062..536cf7b78078747ef494b21afd5c1585eefc2812 100644 (file)
@@ -231,51 +231,64 @@ AbstractMetaFunctionCList AbstractMetaClass::operatorOverloads(OperatorQueryOpti
     return returned;
 }
 
+static bool isArithmeticOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+    return f->ownerClass() == f->implementingClass()
+           && f->isArithmeticOperator() && !f->isPrivate();
+}
+
 bool AbstractMetaClass::hasArithmeticOperatorOverload() const
 {
-    for (const auto & f: d->m_functions) {
-        if (f->ownerClass() == f->implementingClass() && f->isArithmeticOperator() && !f->isPrivate())
-            return true;
-    }
-    return false;
+    return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+                       isArithmeticOperatorOverload);
+}
+
+static bool isIncDecrementOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+    return f->ownerClass() == f->implementingClass()
+           && f->isIncDecrementOperator() && !f->isPrivate();
 }
 
 bool AbstractMetaClass::hasIncDecrementOperatorOverload() const
 {
-    for (const auto & f: d->m_functions) {
-        if (f->ownerClass() == f->implementingClass()
-            && f->isIncDecrementOperator() && !f->isPrivate()) {
-            return true;
-        }
-    }
-    return false;
+    return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+                       isIncDecrementOperatorOverload);
+}
+
+static bool isBitwiseOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+    return f->ownerClass() == f->implementingClass()
+           && f->isBitwiseOperator() && !f->isPrivate();
 }
 
 bool AbstractMetaClass::hasBitwiseOperatorOverload() const
 {
-    for (const auto & f: d->m_functions) {
-        if (f->ownerClass() == f->implementingClass() && f->isBitwiseOperator() && !f->isPrivate())
-            return true;
-    }
-    return false;
+    return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+                       isBitwiseOperatorOverload);
+}
+
+static bool isComparisonOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+    return f->ownerClass() == f->implementingClass()
+           && f->isComparisonOperator() && !f->isPrivate();
 }
 
 bool AbstractMetaClass::hasComparisonOperatorOverload() const
 {
-    for (const auto &f : d->m_functions) {
-        if (f->ownerClass() == f->implementingClass() && f->isComparisonOperator() && !f->isPrivate())
-            return true;
-    }
-    return false;
+    return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+                       isComparisonOperatorOverload);
+}
+
+static bool isLogicalOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+    return f->ownerClass() == f->implementingClass()
+           && f->isLogicalOperator() && !f->isPrivate();
 }
 
 bool AbstractMetaClass::hasLogicalOperatorOverload() const
 {
-    for (const auto &f : d->m_functions) {
-        if (f->ownerClass() == f->implementingClass() && f->isLogicalOperator() && !f->isPrivate())
-            return true;
-    }
-    return false;
+    return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+                       isLogicalOperatorOverload);
 }
 
 const AbstractMetaFieldList &AbstractMetaClass::fields() const
@@ -885,7 +898,7 @@ void AbstractMetaClass::addSynthesizedComparisonOperators(const AbstractMetaClas
 
     static const char *operators[]
         = {"operator==", "operator!=", "operator<", "operator<=", "operator>", "operator>="};
-    for (auto *op : operators) {
+    for (const auto *op : operators) {
         auto *f = AbstractMetaClassPrivate::createFunction(QLatin1StringView(op),
                                                            AbstractMetaFunction::ComparisonOperator,
                                                            Access::Public, arguments,
@@ -1924,7 +1937,7 @@ void AbstractMetaClass::setSourceLocation(const SourceLocation &sourceLocation)
     d->m_sourceLocation = sourceLocation;
 }
 
-AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr metaClass)
+AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr &metaClass)
 {
     AbstractMetaClassCList result;
     recurseClassHierarchy(metaClass, [&result] (const AbstractMetaClassCPtr &c) {
index 3dc876690527eaf4663f57ac054b22533fe33dda..35946bb049224e8fdaf5d20f7b061f4c6157be0f 100644 (file)
@@ -384,7 +384,7 @@ TypeEntryCPtr parentManagementEntry(const AbstractMetaClassCPtr &klass);
 inline bool hasParentManagement(const AbstractMetaClassCPtr &c)
 { return bool(parentManagementEntry(c)); }
 
-AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr metaClass);
+AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr &metaClass);
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaClass::CppWrapper);
 
index 802f549cf1448aed9c60fe8660bbb15bd201051e..27321ca2d6f4eb8366063ce0c0a9f98fe467250b 100644 (file)
@@ -30,6 +30,7 @@ using AbstractMetaEnumValueList = QList<AbstractMetaEnumValue>;
 using AbstractMetaFieldList = QList<AbstractMetaField>;
 using AbstractMetaFunctionRawPtrList = QList<AbstractMetaFunction *>;
 using AbstractMetaFunctionCList = QList<AbstractMetaFunctionCPtr>;
+using AbstractMetaFunctionList = QList<AbstractMetaFunctionPtr>;
 using AbstractMetaTypeList = QList<AbstractMetaType>;
 using UsingMembers = QList<UsingMember>;
 
index 3ec07509d179de372353c1a09fcb19e3e32aec54..145b040ec1a106b3f6ec38e3ed3e6ca0e75d77ae 100644 (file)
@@ -257,7 +257,7 @@ bool AbstractMetaType::applyArrayModification(QString *errorMessage)
     }
     // Element type to be used for ArrayHandle<>, strip constness.
 
-    auto elementType = new AbstractMetaType(*this);
+    auto *elementType = new AbstractMetaType(*this);
     auto indir = indirectionsV();
     indir.pop_front();
     elementType->setIndirectionsV(indir);
@@ -610,7 +610,7 @@ QString AbstractMetaTypeData::formatSignature(bool minimal) const
     if (m_pattern == AbstractMetaType::ArrayPattern) {
         // Build nested array dimensions a[2][3] in correct order
         result += m_arrayElementType->minimalSignature();
-        const int arrayPos = result.indexOf(u'[');
+        const auto arrayPos = result.indexOf(u'[');
         if (arrayPos != -1)
             result.insert(arrayPos, formatArraySize(m_arrayElementCount));
         else
@@ -620,8 +620,6 @@ QString AbstractMetaTypeData::formatSignature(bool minimal) const
     }
     if (!m_instantiations.isEmpty()) {
         result += u'<';
-        if (minimal)
-            result += u' ';
         const auto size = stripDefaultTemplateArgs(m_typeEntry, m_instantiations);
         for (qsizetype i = 0; i < size; ++i) {
             if (i > 0)
index 9d95b734ccea5e5c58b59a29d4e7072777320301..ac2ca5f344eb6b7f8d268d013c91bd87f0a02759 100644 (file)
@@ -84,7 +84,7 @@ Arguments splitParameters(QStringView paramString, QString *errorMessage)
         // Check defaultValue, "int @b@=5"
         const auto equalPos = t.lastIndexOf(u'=');
         if (equalPos != -1) {
-            const int defaultValuePos = equalPos + 1;
+            const auto defaultValuePos = equalPos + 1;
             argument.defaultValue =
                 t.mid(defaultValuePos, t.size() - defaultValuePos).trimmed().toString();
         }
@@ -92,8 +92,8 @@ Arguments splitParameters(QStringView paramString, QString *errorMessage)
         // Check @name@
         const auto atPos = typeString.indexOf(u'@');
         if (atPos != -1) {
-            const int namePos = atPos + 1;
-            const int nameEndPos = typeString.indexOf(u'@', namePos);
+            const auto namePos = atPos + 1;
+            const auto nameEndPos = typeString.indexOf(u'@', namePos);
             if (nameEndPos == -1) {
                 if (errorMessage != nullptr) {
                     *errorMessage = u"Mismatched @ in \""_s
@@ -142,8 +142,8 @@ AddedFunction::AddedFunctionPtr
         ? callOperator.size() : 0;
     const auto openParenPos = signature.indexOf(u'(', parenSearchStartPos);
     if (openParenPos < 0) {
-        return AddedFunctionPtr(new AddedFunction(signature.toString(),
-                                                  arguments, returnType));
+        return std::make_shared<AddedFunction>(signature.toString(),
+                                               arguments, returnType);
     }
 
     const QString name = signature.left(openParenPos).trimmed().toString();
index 35d2d535a4a2da0fb1bf431f3dbbf29cd942eb4f..c7a2245337575c580a240321abf37a57eb0e6c7d 100644 (file)
@@ -54,3 +54,25 @@ bool asv_contains(QAnyStringView asv, const char *needle)
 {
     return asv.visit([needle](auto s) { return asv_containsImpl(s, needle); });
 }
+
+static qsizetype asv_indexOfImpl(QLatin1StringView v, const char *needle)
+{
+    return v.indexOf(QLatin1StringView(needle));
+}
+
+static qsizetype asv_indexOfImpl(QUtf8StringView v, const char *needle)
+{
+    const char *data = v.data();
+    const char *match = std::strstr(data, needle);
+    return match != nullptr ? qsizetype(match - data) : qsizetype(-1);
+}
+
+static qsizetype asv_indexOfImpl(QStringView v, const char *needle)
+{
+    return v.indexOf(QLatin1StringView(needle));
+}
+
+qsizetype asv_indexOf(QAnyStringView asv, const char *needle)
+{
+    return asv.visit([needle](auto s) { return asv_indexOfImpl(s, needle); });
+}
index e1e6ab7f00713588603ad75c4989ee8c07a2caf1..f1c49e041ef2cd7d1704139980b21eaf609f9ad4 100644 (file)
@@ -4,6 +4,7 @@
 #ifndef ANYSTRINGVIEW_STREAM_H
 #define ANYSTRINGVIEW_STREAM_H
 
+#include <QtCore/QtTypes>
 #include <QtCore/QtClassHelperMacros>
 
 QT_FORWARD_DECLARE_CLASS(QAnyStringView)
@@ -14,5 +15,6 @@ QTextStream &operator<<(QTextStream &str, QAnyStringView asv);
 
 bool asv_contains(QAnyStringView asv, char needle);
 bool asv_contains(QAnyStringView asv, const char *needle);
+qsizetype asv_indexOf(QAnyStringView asv, const char *needle);
 
 #endif // ANYSTRINGVIEW_STREAM_H
index a4efaf2f9786d1d33499c2f6d662d8a6aafc214e..f3d9d0b5899118cca87d427e7a79ba20529bc232 100644 (file)
@@ -11,6 +11,7 @@
 #include "abstractmetalang.h"
 #include "codesnip.h"
 #include "exception.h"
+#include "graph.h"
 #include "messages.h"
 #include "modifications.h"
 #include "optionsparser.h"
@@ -241,7 +242,7 @@ struct ApiExtractorPrivate : public ApiExtractorOptions
                                                   const QString &code,
                                                   bool toPythonMacro);
     void addInstantiatedSmartPointer(InstantiationCollectContext &context,
-                                     const AbstractMetaType &type);
+                                     const AbstractMetaType &type) const;
 
     AbstractMetaBuilder *m_builder = nullptr;
 };
@@ -401,6 +402,48 @@ bool ApiExtractorPrivate::runHelper(ApiExtractorFlags flags)
     return result;
 }
 
+static qsizetype indexOfPointee(const InstantiatedSmartPointers &instantiatedList,
+                                const AbstractMetaClassCPtr &pointee)
+{
+    for (qsizetype i = 0, size = instantiatedList.size(); i < size; ++i) {
+        if (instantiatedList.at(i).pointee == pointee)
+            return i;
+    }
+    return -1;
+}
+
+// Sort the list of instantiated smart pointers such that base classes go before
+// descendant classes since those register conversions to smart_ptr<base> for
+// which the base definition needs to exist (PYSIDE-2946).
+static InstantiatedSmartPointers
+    topologicalSortSmartPointers(const InstantiatedSmartPointers &instantiatedList)
+{
+    const auto size = instantiatedList.size();
+    if (size < 2)
+        return instantiatedList;
+
+    // Create a graph (using int indexes for sorting) with dependency edges for the inheritance
+    // within the pointee classes list.
+    Graph<InstantiatedSmartPointer> graph(instantiatedList);
+    for (qsizetype i = 0; i < size; ++i) {
+        const auto &smp = instantiatedList.at(i);
+        if (smp.pointee) {
+            for (const auto &base : smp.pointee->baseClasses()) {
+                const auto baseIndex = indexOfPointee(instantiatedList, base);
+                if (baseIndex != -1)
+                    graph.addEdgeByIndexes(baseIndex, i);
+            }
+        }
+    }
+
+    const auto sortedGraphResult = graph.topologicalSort();
+    // Should not really fail since it is only by base classes ATM...
+    if (!sortedGraphResult.isValid())
+        throw Exception("Failed to sort the list of instantiated smart pointers (cyclic dependency?)"_L1);
+
+    return sortedGraphResult.result;
+}
+
 static inline void classListToCList(const AbstractMetaClassList &list, AbstractMetaClassCList *target)
 {
     target->reserve(list.size());
@@ -423,7 +466,15 @@ std::optional<ApiExtractorResult> ApiExtractor::run(ApiExtractorFlags flags)
     result.m_flags = flags;
     result.m_typedefTargetToName = d->m_builder->typedefTargetToName();
     qSwap(result.m_instantiatedContainers, collectContext.instantiatedContainers);
-    qSwap(result.m_instantiatedSmartPointers, collectContext.instantiatedSmartPointers);
+
+    // Populate the list of pointees by type entry.
+    for (auto &smp : collectContext.instantiatedSmartPointers) {
+        const auto instantiationTe = smp.type.instantiations().constFirst().typeEntry();
+        if (instantiationTe->isComplex())
+            smp.pointee = AbstractMetaClass::findClass(result.m_metaClasses, instantiationTe);
+    }
+    result.m_instantiatedSmartPointers =
+        topologicalSortSmartPointers(collectContext.instantiatedSmartPointers);
     return result;
 }
 
@@ -464,7 +515,7 @@ AbstractMetaClassPtr ApiExtractor::inheritTemplateClass(const ComplexTypeEntryPt
 
 QString ApiExtractorPrivate::getSimplifiedContainerTypeName(const AbstractMetaType &type)
 {
-    const QString signature = type.cppSignature();
+    QString signature = type.cppSignature();
     if (!type.typeEntry()->isContainer() && !type.typeEntry()->isSmartPointer())
         return signature;
     QString typeName = signature;
@@ -593,7 +644,7 @@ static void addOwnerModification(const AbstractMetaFunctionCList &functions,
 }
 
 void ApiExtractorPrivate::addInstantiatedSmartPointer(InstantiationCollectContext &context,
-                                                      const AbstractMetaType &type)
+                                                      const AbstractMetaType &type) const
 {
     InstantiatedSmartPointer smp;
     smp.type =  canonicalSmartPtrInstantiation(type);
@@ -606,6 +657,15 @@ void ApiExtractorPrivate::addInstantiatedSmartPointer(InstantiationCollectContex
     QString name = ste->getTargetName(smp.type);
     auto parentTypeEntry = ste->parent();
 
+    // FIXME PYSIDE 7: Make global scope the default behavior?
+    // Note: Also requires changing SmartPointerTypeEntry::getTargetName()
+    // to not strip namespaces from unnamed instances.
+    const bool globalScope = name.startsWith("::"_L1);
+    if (globalScope) {
+        name.remove(0, 2);
+        parentTypeEntry = typeSystemTypeEntry(ste);
+    }
+
     auto colonPos = name.lastIndexOf(u"::");
     const bool withinNameSpace = colonPos != -1;
     if (withinNameSpace) { // user defined
@@ -652,7 +712,7 @@ ApiExtractorPrivate::collectInstantiatedContainersAndSmartPointers(Instantiation
 {
     addInstantiatedContainersAndSmartPointers(context, func->type(), func->signature());
     for (const AbstractMetaArgument &arg : func->arguments()) {
-        const auto argType = arg.type();
+        const auto &argType = arg.type();
         const auto type = argType.viewOn() != nullptr ? *argType.viewOn() : argType;
         addInstantiatedContainersAndSmartPointers(context, type, func->signature());
     }
@@ -789,7 +849,7 @@ ApiExtractorPrivate::collectContainerTypesFromConverterMacros(InstantiationColle
     qsizetype start = 0;
     QString errorMessage;
     while ((start = code.indexOf(convMacro, start)) != -1) {
-        int end = code.indexOf(u']', start);
+        const auto end = code.indexOf(u']', start);
         start += offset;
         if (code.at(start) != u'%') {
             QString typeString = code.mid(start, end - start);
index 7bff5c25297dbc083f1de1c90715a4e943696386..a15005e4436a4c512650325d153b021a0143968d 100644 (file)
@@ -84,4 +84,3 @@ private:
 };
 
 #endif // APIEXTRACTOR_H
-
index b2aae88ed1cf70877d00ca38310073aaad133c49..d862286a066fa1f36fb22375165141235668e8ae 100644 (file)
@@ -20,6 +20,7 @@ struct InstantiatedSmartPointer
 {
     AbstractMetaClassCPtr smartPointer; // Template class
     AbstractMetaClassCPtr specialized; // Specialized for type
+    AbstractMetaClassCPtr pointee;
     AbstractMetaType type;
 };
 
index 31e7efb05555805f8fd71b5888c8338a75edf074..06216c2eaaf6629caf6f7cfc65bd8821baec8264 100644 (file)
@@ -19,9 +19,6 @@
 #include <QtCore/QStack>
 #include <QtCore/QList>
 
-#include <cstring>
-#include <ctype.h>
-
 using namespace Qt::StringLiterals;
 
 namespace clang {
@@ -269,7 +266,7 @@ static QString msgCannotDetermineException(const std::string_view &snippetV)
 {
     const auto newLine = snippetV.find('\n'); // Multiline noexcept specifications have been found in Qt
     const bool truncate = newLine != std::string::npos;
-    const qsizetype length = qsizetype(truncate ? newLine : snippetV.size());
+    const auto length = qsizetype(truncate ? newLine : snippetV.size());
     QString snippet = QString::fromUtf8(snippetV.data(), length);
     if (truncate)
         snippet += "..."_L1;
@@ -441,14 +438,14 @@ void BuilderPrivate::addField(const CXCursor &cursor)
 static QStringList qualifiedName(const QString &t)
 {
     QStringList result;
-    int end = t.indexOf(u'<');
+    auto end = t.indexOf(u'<');
     if (end == -1)
         end = t.indexOf(u'(');
     if (end == -1)
         end = t.size();
-    int lastPos = 0;
+    qsizetype lastPos = 0;
     while (true) {
-        const int nextPos = t.indexOf(u"::"_s, lastPos);
+        const auto nextPos = t.indexOf(u"::"_s, lastPos);
         if (nextPos < 0 || nextPos >= end)
             break;
         result.append(t.mid(lastPos, nextPos - lastPos));
@@ -579,7 +576,7 @@ TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type,
     if (m_currentClass && typeName.startsWith(u"type-parameter-0-")) {
         if (cacheable != nullptr)
             *cacheable = false;
-        bool ok;
+        bool ok{};
         const int n = QStringView{typeName}.mid(17).toInt(&ok);
         if (ok) {
             auto currentTemplate = currentTemplateClass();
@@ -658,7 +655,7 @@ QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &c
     const std::string_view snippet = bv->getCodeSnippet(cursor);
     auto equalSign = snippet.find('=');
     if (equalSign == std::string::npos)
-        return QString();
+        return {};
     ++equalSign;
     QString result = QString::fromLocal8Bit(snippet.data() + equalSign,
                                             qsizetype(snippet.size() - equalSign));
@@ -726,7 +723,7 @@ std::pair<QString, ClassModelItem> BuilderPrivate::getBaseClass(CXType type) con
     // "std::vector<T>").
     const QStringList &baseScope = it.value()->scope();
     if (!baseScope.isEmpty()) {
-        const int lastSep = baseClassName.lastIndexOf(u"::"_s);
+        const auto lastSep = baseClassName.lastIndexOf(u"::"_s);
         if (lastSep >= 0)
             baseClassName.remove(0, lastSep + u"::"_s.size());
         baseClassName.prepend(u"::"_s);
@@ -755,9 +752,9 @@ void BuilderPrivate::setFileName(const CXCursor &cursor, _CodeModelItem *item)
     }
 }
 
-Builder::Builder()
+Builder::Builder() :
+    d(new BuilderPrivate(this))
 {
-    d = new BuilderPrivate(this);
 }
 
 Builder::~Builder()
index b2ec6d3046f3dfc5a4b3def49e8bd7f54bf77fe4..218aa6163a869eb2b56396a43337bdc3cd52f05a 100644 (file)
@@ -17,7 +17,7 @@ public:
     Q_DISABLE_COPY_MOVE(Builder)
 
     Builder();
-    ~Builder();
+    ~Builder() override;
 
     void setForceProcessSystemIncludes(const QStringList &systemIncludes);
 
index 3c002da9c0d9e059aac147eb4a7930670e629175..7c69963733534ef1424c64d55c821b8cfc079182 100644 (file)
@@ -146,10 +146,10 @@ QDebug operator<<(QDebug s, const CXSourceLocation &location)
 {
     QDebugStateSaver saver(s);
     s.nospace();
-    CXFile file; // void *
-    unsigned line;
-    unsigned column;
-    unsigned offset;
+    CXFile file{}; // void *
+    unsigned line{};
+    unsigned column{};
+    unsigned offset{};
     clang_getExpansionLocation(location, &file, &line, &column, &offset);
     const CXString cxFileName = clang_getFileName(file);
     // Has been observed to be 0 for invalid locations
index da6930476cd2bd19d97ccc991b917c73406415b7..43334c30648f455609d021fab5ba245e58b87707 100644 (file)
@@ -127,10 +127,10 @@ std::string_view BaseVisitor::getCodeSnippet(const CXCursor &cursor)
 
 bool BaseVisitor::_handleVisitLocation(const CXSourceLocation &location)
 {
-    CXFile cxFile; // void *
-    unsigned line;
-    unsigned column;
-    unsigned offset;
+    CXFile cxFile{}; // void *
+    unsigned line{};
+    unsigned column{};
+    unsigned offset{};
     clang_getExpansionLocation(location, &cxFile, &line, &column, &offset);
 
     if (cxFile == m_currentCxFile) // Same file?
@@ -235,6 +235,7 @@ static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsi
 static CXTranslationUnit createTranslationUnit(CXIndex index,
                                                const QByteArrayList &args,
                                                bool addCompilerSupportArguments,
+                                               LanguageLevel level,
                                                unsigned flags = 0)
 {
     // courtesy qdoc
@@ -255,7 +256,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index,
 
     QByteArrayList clangArgs;
     if (addCompilerSupportArguments) {
-        clangArgs += emulatedCompilerOptions();
+        clangArgs += emulatedCompilerOptions(level);
         clangArgs += defaultArgs;
     }
     clangArgs += detectVulkan();
@@ -263,7 +264,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index,
     QScopedArrayPointer<const char *> argv(byteArrayListToFlatArgV(clangArgs));
     qDebug().noquote().nospace() << msgCreateTranslationUnit(clangArgs, flags);
 
-    CXTranslationUnit tu;
+    CXTranslationUnit tu{};
     CXErrorCode err = clang_parseTranslationUnit2(index, nullptr, argv.data(),
                                                   clangArgs.size(), nullptr, 0,
                                                   defaultFlags | flags, &tu);
@@ -280,7 +281,7 @@ static CXTranslationUnit createTranslationUnit(CXIndex index,
  */
 
 bool parse(const QByteArrayList  &clangArgs, bool addCompilerSupportArguments,
-           unsigned clangFlags, BaseVisitor &bv)
+           LanguageLevel level, unsigned clangFlags, BaseVisitor &bv)
 {
     CXIndex index = clang_createIndex(0 /* excludeDeclarationsFromPCH */,
                                       1 /* displayDiagnostics */);
@@ -291,7 +292,7 @@ bool parse(const QByteArrayList  &clangArgs, bool addCompilerSupportArguments,
 
     CXTranslationUnit translationUnit =
         createTranslationUnit(index, clangArgs, addCompilerSupportArguments,
-                              clangFlags);
+                              level, clangFlags);
     if (!translationUnit)
         return false;
 
index 4a46248e4feba409d93b62740318544aa8412f98..22e0a50cd6f8d3b787731b0063c3df90eef4019f 100644 (file)
@@ -14,6 +14,8 @@
 #include <string_view>
 #include <utility>
 
+enum class LanguageLevel;
+
 namespace clang {
 
 struct Diagnostic;
@@ -79,7 +81,7 @@ private:
 
 bool parse(const QByteArrayList  &clangArgs,
            bool addCompilerSupportArguments,
-           unsigned clangFlags, BaseVisitor &ctx);
+           LanguageLevel level, unsigned clangFlags, BaseVisitor &ctx);
 
 } // namespace clang
 
index 1651e09ece8ed279530edc679486e3e9d1ce4605..65d02d18040b03723412cb619e23ea2dc171d3e6 100644 (file)
@@ -61,10 +61,10 @@ SourceLocation getCursorLocation(const CXCursor &cursor)
 
 CXString getFileNameFromLocation(const CXSourceLocation &location)
 {
-    CXFile file;
-    unsigned line;
-    unsigned column;
-    unsigned offset;
+    CXFile file{};
+    unsigned line{};
+    unsigned column{};
+    unsigned offset{};
     clang_getExpansionLocation(location, &file, &line, &column, &offset);
     return clang_getFileName(file);
 }
index f4d04eac550337b15ab17aef5ef45c0e92d6c0bc..20224020b9b29deb75d85740acb5c9db8f2d5782 100644 (file)
@@ -376,14 +376,15 @@ static void appendClangBuiltinIncludes(HeaderPaths *p)
 }
 
 // Returns clang options needed for emulating the host compiler
-QByteArrayList emulatedCompilerOptions()
+QByteArrayList emulatedCompilerOptions(LanguageLevel level)
 {
     QByteArrayList result;
     HeaderPaths headerPaths;
     switch (compiler()) {
     case Compiler::Msvc:
         result.append("-fms-compatibility-version="_ba + msvcCompatVersion());
-        result.append(QByteArrayLiteral("-fdelayed-template-parsing"));
+        if (level < LanguageLevel::Cpp20)
+            result.append("-fdelayed-template-parsing"_ba);
         result.append(QByteArrayLiteral("-Wno-microsoft-enum-value"));
         result.append("/Zc:__cplusplus"_ba);
         // Fix yvals_core.h:  STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update)
index 462e8f20547531f6fcf7d569affe7eb121d3e5a9..f1d63b7c36bcef873d54f3cdeeba539c22f3f76f 100644 (file)
@@ -33,7 +33,7 @@ enum class Platform {
 namespace clang {
 QVersionNumber libClangVersion();
 
-QByteArrayList emulatedCompilerOptions();
+QByteArrayList emulatedCompilerOptions(LanguageLevel level);
 LanguageLevel emulatedCompilerLanguageLevel();
 
 const char *languageLevelOption(LanguageLevel l);
index 637e4a422dc6874fe9cf07d411b1dfe26423c270..54f721315b4e0190a3c3612a3bd612d1f13dfb49 100644 (file)
@@ -137,8 +137,11 @@ static void parseWebXmlElement(WebXmlCodeTag tag, const QXmlStreamAttributes &at
     }
         break;
     case WebXmlCodeTag::Parameter:
-        Q_ASSERT(!cd->functions.isEmpty());
-        cd->functions.last().parameters.append(attributes.value(u"type"_s).toString());
+        Q_ASSERT(!cd->functions.isEmpty()); {
+        FunctionDocumentation &last = cd->functions.last();
+        last.parameters.append(attributes.value(u"type"_s).toString());
+        last.parameterNames.append(attributes.value(u"name"_s).toString());
+    }
         break;
     case WebXmlCodeTag::Property: {
         PropertyDocumentation pd;
@@ -192,23 +195,15 @@ static QString msgXmlError(const QString &fileName, const QXmlStreamReader &read
     return result;
 }
 
-std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage)
+static bool parseWebXmlHelper(QFile *file, ClassDocumentation *result, QString *errorMessage)
 {
-    ClassDocumentation result;
-
-    QFile file(fileName);
-    if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
-        *errorMessage = msgCannotOpenForReading(file);
-        return std::nullopt;
-    }
-
     WebXmlCodeTag lastTag = WebXmlCodeTag::Other;
-    QXmlStreamReader reader(&file);
+    QXmlStreamReader reader(file);
     while (!reader.atEnd()) {
         switch (reader.readNext()) {
         case QXmlStreamReader::StartElement: {
             const auto currentTag = tag(reader.name());
-            parseWebXmlElement(currentTag, reader.attributes(), &result);
+            parseWebXmlElement(currentTag, reader.attributes(), result);
             switch (currentTag) { // Store relevant tags in lastTag
             case WebXmlCodeTag::Class:
             case WebXmlCodeTag::Function:
@@ -222,16 +217,16 @@ std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *
                 QString *target = nullptr;
                 switch (lastTag) {
                 case WebXmlCodeTag::Class:
-                    target = &result.description;
+                    target = &result->description;
                     break;
                 case WebXmlCodeTag::Function:
-                    target = &result.functions.last().description;
+                    target = &result->functions.last().description;
                     break;
                 case WebXmlCodeTag::Enum:
-                    target = &result.enums.last().description;
+                    target = &result->enums.last().description;
                     break;
                 case WebXmlCodeTag::Property:
-                    target = &result.properties.last().description;
+                    target = &result->properties.last().description;
                 default:
                     break;
                 }
@@ -249,14 +244,52 @@ std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *
     }
 
     if (reader.error() != QXmlStreamReader::NoError) {
-        *errorMessage= msgXmlError(fileName, reader);
-        return std::nullopt;
+        *errorMessage= msgXmlError(file->fileName(), reader);
+        return false;
+    }
+
+    return result;
+}
+
+std::optional<ClassDocumentation> parseWebXml(const QStringList &fileNames, QString *errorMessage)
+{
+    ClassDocumentation result;
+    for (const auto &fileName : fileNames) {
+        QFile file(fileName);
+        if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
+            *errorMessage = msgCannotOpenForReading(file);
+            return std::nullopt;
+        }
+        if (!parseWebXmlHelper(&file, &result, errorMessage))
+            return std::nullopt;
     }
 
     sortDocumentation(&result);
     return result;
 }
 
+// Helpers to remove some sections with information on how to build
+// and link and the C++ reference from the WebXML module description
+static void removeElement(QByteArrayView begin, QByteArrayView end,
+                          QByteArray *data)
+{
+    auto startPos = data->indexOf(begin);
+    if (startPos != -1) {
+        auto endPos = data->indexOf(end, startPos + begin.size());
+        if (endPos != -1)
+            data->remove(startPos, endPos + end.size() - startPos);
+    }
+}
+
+static void removeSection(const QByteArray &id,
+                          QByteArray *data)
+{
+    QByteArray contentBegin = "<contents name=\"" + id + '"';
+    removeElement(contentBegin, "/>"_ba, data);
+    QByteArray sectionBegin = "<section id=\""_ba + id + "\">"_ba;
+    removeElement(sectionBegin, "</section>"_ba, data);
+}
+
 QString webXmlModuleDescription(const QString &fileName, QString *errorMessage)
 {
     QFile file(fileName);
@@ -265,8 +298,21 @@ QString webXmlModuleDescription(const QString &fileName, QString *errorMessage)
         return {};
     }
 
+    QByteArray text = file.readAll();
+    file.close();
+    static const QByteArrayList cppSectionIds{
+        "api-reference"_ba, "building-with-cmake"_ba, "building-with-qmake"_ba,
+        "c-api"_ba, "c-classes"_ba, "examples"_ba, "qml-api"_ba, "reference"_ba,
+        "reference-and-examples"_ba, "using-the-module"_ba
+    };
+    for (const auto &cppSectionId : cppSectionIds)
+        removeSection(cppSectionId, &text);
+
+    QBuffer buffer(&text);
+    buffer.open(QIODevice::ReadOnly);
+
     QString result;
-    QXmlStreamReader reader(&file);
+    QXmlStreamReader reader(&buffer);
     while (!reader.atEnd()) {
         switch (reader.readNext()) {
         case QXmlStreamReader::StartElement:
index d47101389b0d7eff6f382e981c8b0d8efe574af2..df834ff35ec321834e57fbd8b35e889d3a13736b 100644 (file)
@@ -36,6 +36,7 @@ struct FunctionDocumentationQuery
 /// A function in a WebXML/doxygen document
 struct FunctionDocumentation : public FunctionDocumentationQuery
 {
+    QStringList parameterNames;
     QString signature;
     QString returnType;
     QString description;
@@ -68,7 +69,7 @@ struct ClassDocumentation
 };
 
 /// Parse a WebXML class/namespace document
-std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage);
+std::optional<ClassDocumentation> parseWebXml(const QStringList &fileNames, QString *errorMessage);
 
 /// Extract the module description from a WebXML module document
 QString webXmlModuleDescription(const QString &fileName, QString *errorMessage);
index 5b884f2cc7be7e009c861d9a15a866dd4d1d6b4b..f1d711313ef2d5d285894e0269fc0c8312865c43 100644 (file)
@@ -163,6 +163,9 @@ public:
     bool isValueTypeWithCopyConstructorOnly() const;
     void setValueTypeWithCopyConstructorOnly(bool v);
 
+    QString docFile() const;
+    void setDocFile(const QString &docFile);
+
     // FIXME PYSIDE 7: Remove this
     static bool isParentManagementEnabled();
     static void setParentManagementEnabled(bool e);
index b6eda651c6502126018ceb19f4bb5329fd574208..18b4488c3a45908fd5f7375010c1e29b83c31956 100644 (file)
@@ -208,4 +208,3 @@ QDebug operator<<(QDebug dbg, const QXmlStreamAttributes &attrs)
     dbg << ')';
     return dbg;
 }
-
index 6d458b25a95eb331b36e94fa15ef4dd633521738..da3ca8cdadbff07bff99c1af9e410ea967fef965 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "abstractmetalang_typedefs.h"
 #include "modifications_typedefs.h"
+#include "documentation.h"
 
 #include <QtCore/QString>
 
 
 class AbstractMetaClass;
 class DocModification;
-class Documentation;
 
 class XQuery;
 
 struct FunctionDocumentation;
 
+struct ModuleDocumentation
+{
+    Documentation documentation;
+    QString qmlTypesUrl;
+};
+
 class DocParser
 {
 public:
@@ -27,17 +33,10 @@ public:
 
     DocParser();
     virtual ~DocParser();
-    virtual void fillDocumentation(const AbstractMetaClassPtr &metaClass) = 0;
+    virtual QString fillDocumentation(const AbstractMetaClassPtr &metaClass) = 0;
     virtual void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f);
     virtual void fillGlobalEnumDocumentation(AbstractMetaEnum &e);
 
-    /**
-     *   Process and retrieves documentation concerning the entire
-     *   module or library.
-     *   \return object containing module/library documentation information
-     */
-    virtual Documentation retrieveModuleDocumentation() = 0;
-
     void setDocumentationDataDirectory(const QString& dir)
     {
         m_docDataDir = dir;
@@ -55,34 +54,6 @@ public:
         return m_docDataDir;
     }
 
-    void setLibrarySourceDirectory(const QString& dir)
-    {
-        m_libSourceDir = dir;
-    }
-    /**
-     *   Informs the location of the library being parsed. The library
-     *   source code is parsed for the documentation comments.
-     *   \return the path for the directory containing the source code of
-     *   the library beign parsed.
-     */
-    QString librarySourceDirectory() const
-    {
-        return m_libSourceDir;
-    }
-
-    void setPackageName(const QString& packageName)
-    {
-        m_packageName = packageName;
-    }
-    /**
-     *   Retrieves the name of the package (or module or library) being parsed.
-     *   \return the name of the package (module/library) being parsed
-     */
-    QString packageName() const
-    {
-        return m_packageName;
-    }
-
     /**
     *   Process and retrieves documentation concerning the entire
     *   module or library.
@@ -90,7 +61,7 @@ public:
     *   \return object containing module/library documentation information
     *   \todo Merge with retrieveModuleDocumentation() on next ABI change.
     */
-    virtual Documentation retrieveModuleDocumentation(const QString& name) = 0;
+    virtual ModuleDocumentation retrieveModuleDocumentation(const QString &name) = 0;
 
     static bool skipForQuery(const AbstractMetaFunctionCPtr &func);
 
@@ -115,9 +86,7 @@ protected:
     static QString applyDocModifications(const DocModificationList &xpathMods, const QString &xml);
 
 private:
-    QString m_packageName;
     QString m_docDataDir;
-    QString m_libSourceDir;
 
     static QString execXQuery(const XQueryPtr &xquery, const QString &query) ;
 };
index 33cf0e9fb0cb696b761ef935b0618986f0e36cd8..7d9054af753bbfe9dfce87144fb80735a8c43332 100644 (file)
@@ -7,8 +7,12 @@
 
 Documentation::Documentation(const QString &detailed,
                              const QString &brief,
-                             Format fmt) :
-    m_detailed(detailed.trimmed()), m_brief(brief.trimmed()), m_format(fmt)
+                             const QString &sourceFile,
+                             DocumentationFormat fmt) :
+    m_detailed(detailed.trimmed()),
+    m_brief(brief.trimmed()),
+    m_sourceFile(sourceFile),
+    m_format(fmt)
 {
 }
 
@@ -17,20 +21,20 @@ bool Documentation::isEmpty() const
     return m_detailed.isEmpty() && m_brief.isEmpty();
 }
 
-Documentation::Format Documentation::format() const
+DocumentationFormat Documentation::format() const
 {
     return m_format;
 }
 
-void Documentation::setValue(const QString &value, Documentation::Type t)
+void Documentation::setValue(const QString &value, DocumentationType t)
 {
-    if (t == Brief)
+    if (t == DocumentationType::Brief)
         setBrief(value);
     else
         setDetailed(value);
 }
 
-void Documentation::setFormat(Documentation::Format f)
+void Documentation::setFormat(DocumentationFormat f)
 {
     m_format = f;
 }
@@ -53,7 +57,9 @@ QDebug operator<<(QDebug debug, const Documentation &d)
     debug.nospace();
     debug << "Documentation(";
     if (!d.isEmpty()) {
-        debug << "format=" << d.format();
+        debug << "format=" << (d.format() == DocumentationFormat::Native ? "native" : "target");
+        if (d.hasSourceFile())
+            debug << ", file=\"" << d.sourceFile() << '"';
         if (!d.brief().isEmpty())
             debug << ", brief=\"" << d.brief() << '"';
         if (!d.detailed().isEmpty())
index df9d5d614b0afd9f4a7cf5401e9aa9d16331d239..26e7b22ae03442420124d19894f0e11389462c63 100644 (file)
@@ -4,6 +4,8 @@
 #ifndef DOCUMENTATION_H
 #define DOCUMENTATION_H
 
+#include "documentation_enums.h"
+
 #include <QtCore/QString>
 #include <QtCore/QtCompare>
 
@@ -12,28 +14,18 @@ QT_FORWARD_DECLARE_CLASS(QDebug)
 class Documentation
 {
 public:
-    enum Format {
-        Native, // XML
-        Target  // RST
-    };
-
-    enum Type {
-        Detailed,
-        Brief,
-        Last
-    };
-
     Documentation() = default;
     explicit Documentation(const QString &detailed,
                            const QString &brief,
-                           Format fmt = Documentation::Native);
+                           const QString &sourceFile,
+                           DocumentationFormat fmt = DocumentationFormat::Native);
 
     bool isEmpty() const;
 
-    void setValue(const QString& value, Type t = Documentation::Detailed);
+    void setValue(const QString& value, DocumentationType t = DocumentationType::Detailed);
 
-    Documentation::Format format() const;
-    void setFormat(Format f);
+    DocumentationFormat format() const;
+    void setFormat(DocumentationFormat f);
 
     bool equals(const Documentation &rhs) const;
 
@@ -44,18 +36,23 @@ public:
     const QString &brief() const { return m_brief; }
     void setBrief(const QString &brief);
 
+    bool hasSourceFile() const { return !m_sourceFile.isEmpty(); }
+    const QString &sourceFile() const { return m_sourceFile; }
+    void setSourceFile(const QString &newSourceFile) { m_sourceFile = newSourceFile; }
+
 private:
     friend bool comparesEqual(const Documentation &lhs,
                               const  Documentation &rhs) noexcept
     {
         return lhs.m_format == rhs.m_format && lhs.m_detailed == rhs.m_detailed
-               && lhs.m_brief == rhs.m_brief;
+               && lhs.m_brief == rhs.m_brief && lhs.m_sourceFile == rhs.m_sourceFile;
     }
     Q_DECLARE_EQUALITY_COMPARABLE(Documentation)
 
     QString m_detailed;
     QString m_brief;
-    Format m_format = Documentation::Native;
+    QString m_sourceFile;
+    DocumentationFormat m_format = DocumentationFormat::Native;
 };
 
 #ifndef QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken6/ApiExtractor/documentation_enums.h b/sources/shiboken6/ApiExtractor/documentation_enums.h
new file mode 100644 (file)
index 0000000..8aeded5
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DOCUMENTATION_ENUMS_H
+#define DOCUMENTATION_ENUMS_H
+
+#include <QtCore/QtTypes>
+
+enum class DocumentationFormat : uint8_t
+{
+    Native, // XML
+    Target  // RST
+};
+
+enum class DocumentationEmphasis : uint8_t
+{
+    None,
+    LanguageNote
+};
+
+enum class DocumentationType : uint8_t
+{
+    Detailed,
+    Brief
+};
+
+#endif // DOCUMENTATION_ENUMS_H
index da790015fd482677e1f35032385cd5ddd72d8901..e3742cab0ac6b11812fb48ef66ea6127219039ed 100644 (file)
@@ -36,15 +36,10 @@ static QString getSectionKindAttr(const AbstractMetaFunctionCPtr &func)
     return kind;
 }
 
-Documentation DoxygenParser::retrieveModuleDocumentation()
-{
-        return retrieveModuleDocumentation(packageName());
-}
-
-void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
+QString DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
 {
     if (!metaClass)
-        return;
+        return {};
 
     QString doxyFileSuffix;
     if (metaClass->enclosingClass()) {
@@ -70,22 +65,23 @@ void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
             << "Can't find doxygen file for class " << metaClass->name() << ", tried: "
             << QDir::toNativeSeparators(documentationDataDirectory())
             <<  "/{struct|class|namespace}"<< doxyFileSuffix;
-        return;
+        return {};
     }
 
     QString errorMessage;
     XQueryPtr xquery = XQuery::create(doxyFilePath, &errorMessage);
     if (!xquery) {
         qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
-        return;
+        return {};
     }
 
-    static const QList<std::pair<Documentation::Type, QString>> docTags = {
-        { Documentation::Brief,  u"briefdescription"_s },
-        { Documentation::Detailed,  u"detaileddescription"_s }
+    static const QList<std::pair<DocumentationType, QString>> docTags = {
+        { DocumentationType::Brief,  u"briefdescription"_s },
+        { DocumentationType::Detailed,  u"detaileddescription"_s }
     };
     // Get class documentation
     Documentation classDoc;
+    classDoc.setSourceFile(doxyFilePath);
 
     for (const auto &tag : docTags) {
         const QString classQuery = u"/doxygen/compounddef/"_s + tag.second;
@@ -140,6 +136,7 @@ void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
             }
         }
         Documentation funcDoc;
+        funcDoc.setSourceFile(doxyFilePath);
         for (const auto &tag : docTags) {
             QString funcQuery(query);
             if (!isProperty) {
@@ -166,9 +163,10 @@ void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
     //Fields
     for (AbstractMetaField &field : metaClass->fields()) {
         if (field.isPrivate())
-            return;
+            continue;
 
         Documentation fieldDoc;
+        fieldDoc.setSourceFile(doxyFilePath);
         for (const auto &tag : docTags) {
             QString query = u"/doxygen/compounddef/sectiondef/memberdef/name[text()=\""_s
                             + field.name() + u"\"]/../"_s + tag.second;
@@ -193,13 +191,14 @@ void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
             qCWarning(lcShibokenDoc, "%s",
                       qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, meta_enum, query)));
         }
-        meta_enum.setDocumentation(Documentation(doc, {}));
+        meta_enum.setDocumentation(Documentation(doc, {}, doxyFilePath));
     }
 
+    return doxyFilePath;
 }
 
-Documentation DoxygenParser::retrieveModuleDocumentation(const QString& name){
-
+ModuleDocumentation DoxygenParser::retrieveModuleDocumentation(const QString &name)
+{
     QString sourceFile = documentationDataDirectory() + u"/indexpage.xml"_s;
 
     if (!QFile::exists(sourceFile)) {
@@ -219,6 +218,5 @@ Documentation DoxygenParser::retrieveModuleDocumentation(const QString& name){
     // Module documentation
     QString query = u"/doxygen/compounddef/detaileddescription"_s;
     const QString doc = getDocumentation(xquery, query, DocModificationList());
-    return Documentation(doc, {});
+    return {Documentation(doc, {}, sourceFile), {}};
 }
-
index 4f6a9e53c9b3fa50c78b433dd4897c6493c83642..7e32101be20d3691d165644f5a7d1ad54636c9d9 100644 (file)
@@ -10,9 +10,8 @@ class DoxygenParser : public DocParser
 {
 public:
     DoxygenParser() = default;
-    void fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
-    Documentation retrieveModuleDocumentation() override;
-    Documentation retrieveModuleDocumentation(const QString& name) override;
+    QString fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
+    ModuleDocumentation retrieveModuleDocumentation(const QString &name) override;
 };
 
 #endif // DOXYGENPARSER_H
index 8d735d5ec0b0526295c1a4bea0207ecdd2e03a15..6ea2622e32afb8c73488d0bc63c55e49de0453a7 100644 (file)
@@ -11,7 +11,7 @@ class AbstractMetaClass;
 class EnclosingClassMixin {
 public:
 
-    const AbstractMetaClassCPtr enclosingClass() const
+    AbstractMetaClassCPtr enclosingClass() const
         { return m_enclosingClass.lock(); }
     void setEnclosingClass(const AbstractMetaClassCPtr &cls)
         { m_enclosingClass = cls; }
index 447a26da033da52deeb98c9aedfb6d447d37c597..22936c201f1940c948d00ca511afc210e341a66c 100644 (file)
@@ -34,14 +34,28 @@ struct GraphSortResult
 template <class Node>
 class Graph
 {
+    using IndexList = QList<qsizetype>;
+
 public:
     using NodeList = QList<Node>;
 
     Graph() = default;
 
-    // Construct from a sequence of nodes
+    // Construct from a QList of nodes (unchecked, does not require operator==())
+    explicit Graph(const NodeList &list) : m_nodes(list)
+    {
+        m_nodeEntries.resize(list.size());
+    }
+
+    // Construct from a sequence of nodes (checks for duplicated nodes using operator==())
     template<class It>
-    explicit Graph(It i1, It i2) { setNodes(i1, i2); }
+    explicit Graph(It i1, It i2)
+    {
+        const auto size = std::distance(i1, i2);
+        m_nodes.reserve(size);
+        m_nodeEntries.reserve(size);
+        setNodes(i1, i2);
+    }
 
     template<class It>
     void setNodes(It i1, It i2)
@@ -53,22 +67,29 @@ public:
     bool addNode(Node n);
 
     /// Returns whether node was registered
-    bool hasNode(Node node) { return indexOfNode(node) != -1; }
+    bool hasNode(Node node) { return m_nodes.contains(node); }
 
     /// Returns the numbed of nodes in this graph.
     qsizetype nodeCount() const { return m_nodeEntries.size(); }
 
     /// Returns true if the graph contains the edge from -> to
     bool containsEdge(Node from, Node to) const;
+    bool containsEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) const;
     /// Returns true if the graph has any edges
     bool hasEdges() const;
 
     /// Adds an edge to this graph.
     bool addEdge(Node from, Node to);
+    bool addEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex);
     /// Removes an edge from this graph.
     bool removeEdge(Node from, Node to);
+    bool removeEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex);
     /// Clears the graph
-    void clear() { m_nodeEntries.clear(); }
+    void clear()
+    {
+        m_nodes.clear();
+        m_nodeEntries.clear();
+    }
 
     /// Dumps a dot graph to a file named \p filename.
     /// \param fileName file name where the output should be written.
@@ -90,49 +111,37 @@ public:
     GraphSortResult<Node> topologicalSort() const;
 
 private:
-    enum Color { WHITE, GRAY, BLACK };
+    enum Color : quint8 { WHITE, GRAY, BLACK };
 
     struct NodeEntry
     {
-        Node node;
-        NodeList targets;
-        mutable Color color;
+        IndexList targets;
+        mutable Color color = WHITE;
     };
 
     Color colorAt(qsizetype i) const { return m_nodeEntries.at(i).color; }
-    qsizetype indexOfNode(Node n) const;
-    void depthFirstVisit(qsizetype i, NodeList &result) const;
+    void depthFirstVisit(qsizetype i, IndexList *result) const;
 
+    NodeList m_nodes;
     QList<NodeEntry> m_nodeEntries;
 };
 
-template <class Node>
-qsizetype Graph<Node>::indexOfNode(Node n) const
-{
-    for (qsizetype i = 0, size = m_nodeEntries.size(); i < size; ++i) {
-        if (m_nodeEntries.at(i).node == n)
-            return i;
-    }
-    return -1;
-}
-
 template <class Node>
 bool Graph<Node>::addNode(Node n)
 {
     if (hasNode(n))
         return false;
-    m_nodeEntries.append({n, {}, WHITE});
+    m_nodes.append(n);
+    m_nodeEntries.append(NodeEntry{});
     return true;
 }
 
 template <class Node>
-void Graph<Node>::depthFirstVisit(qsizetype i, NodeList &result) const
+void Graph<Node>::depthFirstVisit(qsizetype i, IndexList *result) const
 {
     m_nodeEntries[i].color = GRAY;
 
-    for (auto to : m_nodeEntries[i].targets) {
-        const qsizetype toIndex = indexOfNode(to);
-        Q_ASSERT(toIndex != -1);
+    for (qsizetype toIndex : m_nodeEntries.at(i).targets) {
         switch (colorAt(toIndex)) {
         case WHITE:
             depthFirstVisit(toIndex, result);
@@ -146,7 +155,7 @@ void Graph<Node>::depthFirstVisit(qsizetype i, NodeList &result) const
 
     m_nodeEntries[i].color = BLACK;
 
-    result.append(m_nodeEntries.at(i).node);
+    result->append(i);
 }
 
 template <class Node>
@@ -155,29 +164,29 @@ GraphSortResult<Node> Graph<Node>::topologicalSort() const
     const qsizetype size = m_nodeEntries.size();
 
     GraphSortResult<Node> result;
-    result.result.reserve(size);
 
-    if (hasEdges()) {
+    if (size > 1 && hasEdges()) {
+        result.result.reserve(size);
+        IndexList indexList;
+        indexList.reserve(size);
         for (qsizetype i = 0; i < size; ++i)
             m_nodeEntries[i].color = WHITE;
         for (qsizetype i = 0; i < size; ++i)  {
             if (colorAt(i) == WHITE) // recursive calls may have set it to black
-                depthFirstVisit(i, result.result);
+                depthFirstVisit(i, &indexList);
         }
-    } else { // no edges, shortcut
-        std::transform(m_nodeEntries.cbegin(), m_nodeEntries.cend(),
-                       std::back_inserter(result.result),
-                       [](const NodeEntry &ne) { return ne.node; });
-    }
-
-    if (result.result.size() == size) {
-        std::reverse(result.result.begin(), result.result.end());
-    } else {
-        for (const auto &nodeEntry : m_nodeEntries) {
-            if (!result.result.contains(nodeEntry.node))
-                result.cyclic.append(nodeEntry.node);
+        if (indexList.size() == size) { // Succeeded, all traversed
+            for (qsizetype i = size - 1; i >= 0; --i)
+                result.result.append(m_nodes.at(indexList.at(i)));
+        } else { // Cyclic, Not a DAG!
+            for (qsizetype i = 0; i < size; ++i) {
+                if (!indexList.contains(i))
+                    result.cyclic.append(m_nodes.at(i));
+            }
         }
-        result.result.clear(); // Not a DAG!
+    } else { // no edges, shortcut. Legacy behavior: Also reverse in this case.
+        result.result = m_nodes;
+        std::reverse(result.result.begin(), result.result.end());
     }
     return result;
 }
@@ -185,41 +194,59 @@ GraphSortResult<Node> Graph<Node>::topologicalSort() const
 template <class Node>
 bool Graph<Node>::containsEdge(Node from, Node to) const
 {
-    const qsizetype i = indexOfNode(from);
-    return i != -1 && m_nodeEntries.at(i).targets.contains(to);
+    return containsEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to));
+}
+
+template <class Node>
+bool Graph<Node>::containsEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) const
+{
+    return fromIndex >= 0 && fromIndex < m_nodeEntries.size()
+        && m_nodeEntries.at(fromIndex).targets.contains(toIndex);
 }
 
 template <class Node>
 bool Graph<Node>::hasEdges() const
 {
-    for (const auto &nodeEntry : m_nodeEntries) {
-        if (!nodeEntry.targets.isEmpty())
-            return true;
-    }
-    return false;
+    auto hashEdgesPred = [](const NodeEntry &nodeEntry) { return !nodeEntry.targets.isEmpty(); };
+    return std::any_of(m_nodeEntries.cbegin(), m_nodeEntries.cend(), hashEdgesPred);
 }
 
 template <class Node>
 bool Graph<Node>::addEdge(Node from, Node to)
 {
-    const qsizetype i = indexOfNode(from);
-    if (i == -1 || !hasNode(to) || m_nodeEntries.at(i).targets.contains(to))
+    return addEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to));
+}
+
+template <class Node>
+bool Graph<Node>::addEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex)
+{
+    if (fromIndex < 0 || fromIndex >= m_nodeEntries.size()
+        || toIndex < 0 || toIndex >= m_nodeEntries.size()
+        || m_nodeEntries.at(fromIndex).targets.contains(toIndex)) {
         return false;
-    m_nodeEntries[i].targets.append(to);
+    }
+    m_nodeEntries[fromIndex].targets.append(toIndex);
     return true;
 }
 
 template <class Node>
 bool Graph<Node>::removeEdge(Node from, Node to)
 {
-    const qsizetype i = indexOfNode(from);
-    if (i == -1)
+    return removeEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to));
+}
+
+template <class Node>
+bool Graph<Node>::removeEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex)
+{
+    if (fromIndex < 0 || fromIndex >= m_nodeEntries.size()
+        || toIndex < 0 || toIndex >= m_nodeEntries.size()) {
         return false;
-    auto &targets = m_nodeEntries[i].targets;
-    const qsizetype toIndex = targets.indexOf(to);
-    if (toIndex == -1)
+    }
+    auto &targets = m_nodeEntries[fromIndex].targets;
+    const qsizetype toPos = targets.indexOf(toIndex);
+    if (toPos == -1)
         return false;
-    targets.removeAt(toIndex);
+    targets.removeAt(toPos);
     return true;
 }
 
@@ -242,11 +269,12 @@ void Graph<Node>::formatDot(QTextStream &s,
                             NameFunction nameFunction) const
 {
     s << "digraph D {\n";
-    for (const auto &nodeEntry : m_nodeEntries) {
+    for (qsizetype i = 0, size = m_nodes.size(); i < size; ++i) {
+        const auto &nodeEntry = m_nodeEntries.at(i);
         if (!nodeEntry.targets.isEmpty()) {
-            const QString fromName = nameFunction(nodeEntry.node);
-            for (const Node &to : nodeEntry.targets)
-                s << '"' << fromName << "\" -> \"" << nameFunction(to) << "\"\n";
+            const QString fromName = nameFunction(m_nodes.at(i));
+            for (qsizetype i : nodeEntry.targets)
+                s << '"' << fromName << "\" -> \"" << nameFunction(m_nodes.at(i)) << "\"\n";
         }
     }
     s << "}\n";
@@ -271,13 +299,13 @@ void Graph<Node>::format(QDebug &debug) const
         const auto &nodeEntry = m_nodeEntries.at(i);
         if (i)
             debug << ", ";
-        debug << nodeEntry.node;
+        debug << m_nodes.at(i);
         if (!nodeEntry.targets.isEmpty()) {
             debug << " -> [";
             for (qsizetype t = 0, tsize = nodeEntry.targets.size(); t < tsize; ++t) {
                 if (t)
                     debug << ", ";
-                debug << nodeEntry.targets.at(t);
+                debug << m_nodes.at(nodeEntry.targets.at(t));
             }
             debug << ']';
         }
index b1f0b240e38cc56e88a78caf8e426979b53d7509..57b4e75751fb46deb39859473fa45071ec933802 100644 (file)
@@ -6,6 +6,7 @@
 #include "abstractmetafield.h"
 #include "abstractmetafunction.h"
 #include "abstractmetalang.h"
+#include "include.h"
 #include "modifications.h"
 #include "sourcelocation.h"
 #include "typedatabase.h"
@@ -22,6 +23,9 @@
 #include <QtCore/QStringList>
 #include <QtCore/QXmlStreamReader>
 
+#include <algorithm>
+#include <iterator>
+
 using namespace Qt::StringLiterals;
 
 // abstractmetabuilder.cpp
@@ -408,6 +412,16 @@ QString msgStrippingArgument(const FunctionModelItem &f, int i,
     return result;
 }
 
+QString msgStrippingQtDisambiguatedArgument(const FunctionModelItem &f,
+                                            const QString &originalSignature)
+{
+    QString result;
+    QTextStream str(&result);
+    str << f->sourceLocation() << "Stripping last argument of "
+        << originalSignature << " (QT6_DECL_NEW_OVERLOAD_TAIL).";
+    return result;
+}
+
 QString msgEnumNotDefined(const EnumTypeEntryCPtr &t)
 {
     QString result;
@@ -995,3 +1009,47 @@ QString msgInvalidLanguageLevel(const QString &l)
 {
     return u"Invalid argument for language level: \""_s + l + u"\"."_s;
 }
+
+QString msgCannotFindImage(const QString &href, const QString &context,
+                           const QStringList &candidates)
+{
+    QString result = "Cannot resolve image "_L1 + href + " for "_L1 + context
+                     + " (tried "_L1;
+    for (const auto &candidate : candidates)
+        result += QDir::toNativeSeparators(candidate) + u' ';
+    result += ")."_L1;
+    return result;
+}
+
+QString msgNotRelative(const QString &path, const QString &dir)
+{
+    return QDir::toNativeSeparators(path)
+           + " is not under "_L1 + QDir::toNativeSeparators(dir) + u'.';
+}
+
+QString msgCannotCreateDir(const QString &dir)
+{
+    return "Cannot create "_L1 + QDir::toNativeSeparators(dir) + u'.';
+}
+
+QString msgCannotCopy(const QFile &source, const QString &target)
+{
+    return "Cannot copy "_L1 + QDir::toNativeSeparators(source.fileName())
+        + " to "_L1 + QDir::toNativeSeparators(target)
+        + ": "_L1 + source.errorString();
+}
+
+QString msgCannotFindQDocFile(const AbstractMetaClassCPtr &metaClass,
+                              const QStringList &candidates)
+{
+    QStringList nativeCandidates;
+    std::transform(candidates.cbegin(), candidates.cend(), std::back_inserter(nativeCandidates),
+                   QDir::toNativeSeparators);
+    QString result;
+    QTextStream(&result) << "Cannot find qdoc file for "
+        << (metaClass->isNamespace() ? "namespace" : "class") << " \""
+        << metaClass->typeEntry()->qualifiedCppName() << "\" ("
+        << QDir::toNativeSeparators(metaClass->typeEntry()->include().name())
+        << "), tried: " << nativeCandidates.join(", "_L1);
+    return result;
+}
index e3f582b492cdf827fcfeb0e03dfd70c95b78e1e9..a7d491db1ac26d2814aa27d66e7753caf17bede7 100644 (file)
@@ -102,6 +102,9 @@ QString msgStrippingArgument(const FunctionModelItem &f, int i,
                              const ArgumentModelItem &arg,
                              const QString &reason);
 
+QString msgStrippingQtDisambiguatedArgument(const FunctionModelItem &f,
+                                            const QString &originalSignature);
+
 QString msgEnumNotDefined(const EnumTypeEntryCPtr &t);
 
 QString msgUnknownBase(const AbstractMetaClassCPtr &metaClass,
@@ -261,4 +264,16 @@ QString msgMissingProjectFileMarker(const QString &name, const QByteArray &start
 
 QString msgInvalidLanguageLevel(const QString &l);
 
+QString msgCannotFindImage(const QString &href, const QString &context,
+                           const QStringList &candidates);
+
+QString msgNotRelative(const QString &path, const QString &dir);
+
+QString msgCannotCreateDir(const QString &dir);
+
+QString msgCannotCopy(const QFile &source, const QString &target);
+
+QString msgCannotFindQDocFile(const AbstractMetaClassCPtr &metaClass,
+                              const QStringList &candidates);
+
 #endif // MESSAGES_H
index 27a38f1aa8e0460640d6d0d890ef92c38a5c786a..4db5836a729328a39f455129caf42de2a1ba0cef 100644 (file)
@@ -5,6 +5,7 @@
 #define MODIFICATIONS_H
 
 #include "typesystem_enums.h"
+#include "documentation_enums.h"
 #include "modifications_typedefs.h"
 
 #include <QtCore/QList>
@@ -328,15 +329,19 @@ public:
         return m_mode;
     }
 
-    TypeSystem::Language  format() const { return m_format; }
-    void setFormat(TypeSystem::Language f) { m_format = f; }
+    DocumentationFormat format() const { return m_format; }
+    void setFormat(DocumentationFormat f) { m_format = f; }
+
+    DocumentationEmphasis emphasis() const { return m_emphasis; }
+    void setEmphasis(DocumentationEmphasis newEmphasis) { m_emphasis = newEmphasis; }
 
 private:
     QString m_code;
     QString m_xpath;
     QString m_signature;
     TypeSystem::DocModificationMode m_mode = TypeSystem::DocModificationXPathReplace;
-    TypeSystem::Language m_format = TypeSystem::NativeCode;
+    DocumentationFormat m_format = DocumentationFormat::Native;
+    DocumentationEmphasis m_emphasis = DocumentationEmphasis::None;
 };
 
 #endif // MODIFICATIONS_H
index 259a706dc9ff41a53624960676d8afd27789ee2c..32cefdc6fe0315fd318f8c29334f5ef7542a75f7 100644 (file)
@@ -195,7 +195,7 @@ FileModelItem _CodeModelItem::file() const
     return model()->findFile(fileName());
 }
 
-void _CodeModelItem::getStartPosition(int *line, int *column)
+void _CodeModelItem::getStartPosition(int *line, int *column) const
 {
     *line = m_startLine;
     *column = m_startColumn;
@@ -207,7 +207,7 @@ void _CodeModelItem::setStartPosition(int line, int column)
     m_startColumn = column;
 }
 
-void _CodeModelItem::getEndPosition(int *line, int *column)
+void _CodeModelItem::getEndPosition(int *line, int *column) const
 {
     *line = m_endLine;
     *column = m_endColumn;
@@ -347,11 +347,10 @@ void _ClassModelItem::setTemplateParameters(const TemplateParameterList &templat
 
 bool _ClassModelItem::extendsClass(const QString &name) const
 {
-    for (const BaseClass &bc : m_baseClasses) {
-        if (bc.name == name)
-            return true;
-    }
-    return false;
+    return std::any_of(m_baseClasses.cbegin(), m_baseClasses.cend(),
+                       [&name](const BaseClass &bc) {
+                           return bc.name == name;
+                       });
 }
 
 _ClassModelItem::_ClassModelItem(CodeModel *model, int kind)
@@ -704,10 +703,10 @@ _ScopeModelItem::FindEnumByValueReturn
     if (const auto e = scope->findEnumByValueHelper(fullValue, enumValue))
         return e;
 
-    if (auto *enclosingScope = scope->enclosingScope()) {
+    if (const auto *enclosingScope = scope->enclosingScope()) {
         // The enclosing scope may have several sibling namespaces of that name.
         if (searchSiblingNamespaces && scope->kind() == Kind_Namespace) {
-            if (auto *enclosingNamespace = dynamic_cast<const _NamespaceModelItem *>(enclosingScope)) {
+            if (const auto *enclosingNamespace = dynamic_cast<const _NamespaceModelItem *>(enclosingScope)) {
                 for (const auto &sibling : enclosingNamespace->namespaces()) {
                     if (sibling.get() != scope && sibling->name() == scope->name()) {
                         if (const auto e = findEnumByValueRecursion(sibling.get(),
@@ -724,7 +723,7 @@ _ScopeModelItem::FindEnumByValueReturn
     }
 
     // PYSIDE-331: We need to also search the base classes.
-    if (auto *classItem = dynamic_cast<const _ClassModelItem *>(scope)) {
+    if (const auto *classItem = dynamic_cast<const _ClassModelItem *>(scope)) {
         for (const auto &base : classItem->baseClasses()) {
             if (base.klass) {
                 auto *c = base.klass.get();
@@ -769,7 +768,7 @@ _NamespaceModelItem::_NamespaceModelItem(CodeModel *model, const QString &name,
 
 _NamespaceModelItem::~_NamespaceModelItem() = default;
 
-void _NamespaceModelItem::addNamespace(NamespaceModelItem item)
+void _NamespaceModelItem::addNamespace(const NamespaceModelItem &item)
 {
     item->setEnclosingScope(this);
     m_namespaces.append(item);
index b31c09163a41995cab33c43c4b9beeebcb38d8be..8b3831b30620c5cf097b65f882825db7be2b7ccc 100644 (file)
@@ -133,11 +133,11 @@ public:
 
     FileModelItem file() const;
 
-    void getStartPosition(int *line, int *column);
+    void getStartPosition(int *line, int *column) const;
     int startLine() const { return m_startLine; }
     void setStartPosition(int line, int column);
 
-    void getEndPosition(int *line, int *column);
+    void getEndPosition(int *line, int *column) const;
     void setEndPosition(int line, int column);
 
     SourceLocation sourceLocation() const;
@@ -179,7 +179,7 @@ public:
     Q_DISABLE_COPY_MOVE(_ScopeModelItem)
     DECLARE_MODEL_NODE(Scope)
 
-    ~_ScopeModelItem();
+    ~_ScopeModelItem() override;
 
     ClassList classes() const { return m_classes; }
     const EnumList &enums() const { return m_enums; }
@@ -279,7 +279,7 @@ public:
     explicit _ClassModelItem(CodeModel *model, int kind = __node_kind);
     explicit _ClassModelItem(CodeModel *model, const QString &name,
                              int kind = __node_kind);
-    ~_ClassModelItem();
+    ~_ClassModelItem() override;
 
     const QList<BaseClass> &baseClasses() const { return m_baseClasses; }
 
@@ -329,14 +329,14 @@ public:
     explicit _NamespaceModelItem(CodeModel *model, int kind = __node_kind);
     explicit _NamespaceModelItem(CodeModel *model, const QString &name,
                                  int kind = __node_kind);
-    ~_NamespaceModelItem();
+    ~_NamespaceModelItem() override;
 
     const NamespaceList &namespaces() const { return m_namespaces; }
 
      NamespaceType type() const { return m_type; }
      void setType(NamespaceType t) { m_type = t; }
 
-    void addNamespace(NamespaceModelItem item);
+    void addNamespace(const NamespaceModelItem &item);
 
     NamespaceModelItem findNamespace(QAnyStringView name) const;
 
@@ -359,7 +359,7 @@ public:
 
     using _NamespaceModelItem::_NamespaceModelItem;
 
-    ~_FileModelItem();
+    ~_FileModelItem() override;
 };
 
 class _ArgumentModelItem: public _CodeModelItem
@@ -371,7 +371,7 @@ public:
     explicit _ArgumentModelItem(CodeModel *model, int kind = __node_kind);
     explicit _ArgumentModelItem(CodeModel *model, const QString &name,
                                 int kind = __node_kind);
-    ~_ArgumentModelItem();
+    ~_ArgumentModelItem() override;
 
     TypeInfo type() const;
     void setType(const TypeInfo &type);
@@ -406,7 +406,7 @@ public:
     explicit _MemberModelItem(CodeModel *model, int kind = __node_kind);
     explicit _MemberModelItem(CodeModel *model, const QString &name,
                               int kind = __node_kind);
-    ~_MemberModelItem();
+    ~_MemberModelItem() override;
 
     bool isConstant() const;
     void setConstant(bool isConstant);
@@ -605,7 +605,7 @@ public:
 
     explicit _EnumModelItem(CodeModel *model, const QString &name, int kind = __node_kind);
     explicit _EnumModelItem(CodeModel *model, int kind = __node_kind);
-    ~_EnumModelItem();
+    ~_EnumModelItem() override;
 
     Access accessPolicy() const;
     void setAccessPolicy(Access accessPolicy);
@@ -650,7 +650,7 @@ public:
     explicit _EnumeratorModelItem(CodeModel *model, int kind = __node_kind);
     explicit _EnumeratorModelItem(CodeModel *model, const QString &name,
                                   int kind = __node_kind);
-    ~_EnumeratorModelItem();
+    ~_EnumeratorModelItem() override;
 
     QString stringValue() const;
     void setStringValue(const QString &stringValue);
@@ -680,7 +680,7 @@ public:
     explicit _TemplateParameterModelItem(CodeModel *model, int kind = __node_kind);
     explicit _TemplateParameterModelItem(CodeModel *model, const QString &name,
                                          int kind = __node_kind);
-    ~_TemplateParameterModelItem();
+    ~_TemplateParameterModelItem() override;
 
     TypeInfo type() const;
     void setType(const TypeInfo &type);
index bbd5a712d3c04a077704972e81eecb65245b8492..3c600fdde23e1e623461c8201fe46a2b52867934 100644 (file)
@@ -27,7 +27,7 @@ public:
     Type type() { return m_type; }
     qint64 value() const { return m_value; }
     quint64 unsignedValue() const { return m_unsignedValue; }
-    bool isNullValue() const { return m_type == Signed ? m_value == 0 : m_unsignedValue == 0u; }
+    bool isNullValue() const { return m_type == Signed ? m_value == 0 : m_unsignedValue == 0U; }
     bool isNegative() const { return m_type == Signed && m_value < 0; }
 
     void setValue(qint64 v);
index f8c5c31d82f73c9989ab7d5c415b9730cfc914be..06eaf9ec354668c6834f63e218d20c3320ba0400 100644 (file)
@@ -92,7 +92,7 @@ TypeInfo TypeInfo::combine(const TypeInfo &__lhs, const TypeInfo &__rhs)
     if (__rhs.referenceType() > __result.referenceType())
         __result.setReferenceType(__rhs.referenceType());
 
-    const auto indirections = __rhs.indirectionsV();
+    const auto &indirections = __rhs.indirectionsV();
     for (auto i : indirections)
         __result.addIndirection(i);
 
@@ -274,13 +274,12 @@ bool TypeInfo::isPlain() const
 
 TypeInfo TypeInfo::resolveType(TypeInfo const &__type, const ScopeModelItem &__scope)
 {
-    CodeModel *__model = __scope->model();
-    Q_ASSERT(__model != nullptr);
-
-    return TypeInfo::resolveType(__model->findItem(__type.qualifiedName(), __scope),  __type, __scope);
+    return TypeInfo::resolveType(CodeModel::findItem(__type.qualifiedName(), __scope),
+                                 __type, __scope);
 }
 
-TypeInfo TypeInfo::resolveType(CodeModelItem __item, TypeInfo const &__type, const ScopeModelItem &__scope)
+TypeInfo TypeInfo::resolveType(const CodeModelItem &__item, TypeInfo const &__type,
+                               const ScopeModelItem &__scope)
 {
     // Copy the type and replace with the proper qualified name. This
     // only makes sence to do if we're actually getting a resolved
@@ -293,8 +292,8 @@ TypeInfo TypeInfo::resolveType(CodeModelItem __item, TypeInfo const &__type, con
     }
 
     if (TypeDefModelItem __typedef = std::dynamic_pointer_cast<_TypeDefModelItem>(__item)) {
-        const TypeInfo combined = TypeInfo::combine(__typedef->type(), otherType);
-        const CodeModelItem nextItem = __scope->model()->findItem(combined.qualifiedName(), __scope);
+        TypeInfo combined = TypeInfo::combine(__typedef->type(), otherType);
+        const CodeModelItem nextItem = CodeModel::findItem(combined.qualifiedName(), __scope);
         if (!nextItem)
             return combined;
         // PYSIDE-362, prevent recursion on opaque structs like
@@ -302,7 +301,7 @@ TypeInfo TypeInfo::resolveType(CodeModelItem __item, TypeInfo const &__type, con
         if (nextItem.get() ==__item.get()) {
             std::cerr << "** WARNING Bailing out recursion of " << __FUNCTION__
                 << "() on " << qPrintable(__type.qualifiedName().join(u"::"_s))
-                << std::endl;
+                << '\n';
             return otherType;
         }
         return resolveType(nextItem, combined, __scope);
index e4f363b679b10e580e66e23e4d03009944f72fa4..ef57d736d75d9d17bd8be06028a4328dd77628b9 100644 (file)
@@ -118,7 +118,8 @@ private:
 
     friend class TypeInfoTemplateArgumentHandler;
 
-    static TypeInfo resolveType(CodeModelItem item, TypeInfo const &__type, const ScopeModelItem &__scope);
+    static TypeInfo resolveType(const CodeModelItem &item, TypeInfo const &__type,
+                                const ScopeModelItem &__scope);
 };
 
 #ifndef QT_NO_DEBUG_STREAM
index 992f735ac22253df0fa7475f84b96e9bef8b2682..9a9614f0d13ba0b1c60f460dba8eb4240eb4fe8c 100644 (file)
@@ -83,8 +83,8 @@ return %out;
 
 static QString pyDictToCppMap(bool isQMap)
 {
-    return uR"(PyObject *key;
-PyObject *value;
+    return uR"(PyObject *key{};
+PyObject *value{};
 %out.clear();
 Py_ssize_t pos = 0;
 while (PyDict_Next(%in, &pos, &key, &value)) {
@@ -150,8 +150,8 @@ static QString cppMultiHashToPyDict(bool isQMultiHash)
 // Convert Dict of Lists to a STL or Qt multi hash/map
 static QString pyDictToCppMultiHash(bool isQMultiHash)
 {
-    return uR"(PyObject *key;
-    PyObject *values;
+    return uR"(PyObject *key{};
+    PyObject *values{};
     %out.clear();
     Py_ssize_t pos = 0;
     while (PyDict_Next(%in, &pos, &key, &values)) {
@@ -176,14 +176,16 @@ const PredefinedTemplates &predefinedTemplates()
 
     // QPair/std::pair
     {u"shiboken_conversion_pysequence_to_cpppair"_s,
-     uR"(%out.first = %CONVERTTOCPP[%OUTTYPE_0](PySequence_Fast_GET_ITEM(%in, 0));
-%out.second = %CONVERTTOCPP[%OUTTYPE_1](PySequence_Fast_GET_ITEM(%in, 1));
+     uR"(Shiboken::AutoDecRef objfirst(PySequence_GetItem(%in, 0));
+Shiboken::AutoDecRef objsecond(PySequence_GetItem(%in, 1));
+%out.first = %CONVERTTOCPP[%OUTTYPE_0](objfirst);
+%out.second = %CONVERTTOCPP[%OUTTYPE_1](objsecond);
 )"_s},
 
     {u"shiboken_conversion_cpppair_to_pytuple"_s,
     uR"(PyObject *%out = PyTuple_New(2);
-PyTuple_SET_ITEM(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first));
-PyTuple_SET_ITEM(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second));
+PyTuple_SetItem(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first));
+PyTuple_SetItem(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second));
 return %out;
 )"_s},
 
index 34cdd74a9fc33e6cca7f865f594b241278d5955c..32b756fadb3c118d0c0ad90b61f2bab4775c64bc 100644 (file)
@@ -189,14 +189,15 @@ void QPropertySpec::setGenerateGetSetDef(bool generateGetSetDef)
 TypeSystemProperty QPropertySpec::typeSystemPropertyFromQ_Property(const QString &declarationIn,
                                                                    QString *errorMessage)
 {
-    enum class PropertyToken { None, Read, Write, Designable, Reset, Notify };
+    enum class PropertyToken { None, Read, Write, Designable, Reset, Notify, Member };
 
     static const QHash<QString, PropertyToken> tokenLookup = {
         {"READ"_L1, PropertyToken::Read},
         {"WRITE"_L1, PropertyToken::Write},
         {"DESIGNABLE"_L1, PropertyToken::Designable},
         {"RESET"_L1, PropertyToken::Reset},
-        {"NOTIFY"_L1, PropertyToken::Notify}
+        {"NOTIFY"_L1, PropertyToken::Notify},
+        {"MEMBER"_L1, PropertyToken::Member}
     };
 
     errorMessage->clear();
@@ -242,6 +243,10 @@ TypeSystemProperty QPropertySpec::typeSystemPropertyFromQ_Property(const QString
         case PropertyToken::Notify:
             result.notify = propertyTokens.at(pos + 1);
             break;
+        case PropertyToken::Member:
+            // Ignore MEMBER tokens introduced by QTBUG-16852 as Python
+            // properties are anyways generated for fields.
+            return {};
 
         case PropertyToken::None:
             break;
index 5bd99bbd8cbf6346ae307f7c252b40a2c73131d8..daafe685add12561f431bf0a3bedefa260cb9a00 100644 (file)
@@ -9,6 +9,7 @@
 #include "abstractmetalang.h"
 #include "abstractmetatype.h"
 #include "documentation.h"
+#include "exception.h"
 #include "modifications.h"
 #include "messages.h"
 #include "propertyspec.h"
 #include "complextypeentry.h"
 #include "functiontypeentry.h"
 #include "enumtypeentry.h"
+#include "typesystemtypeentry.h"
+#include "typedatabase.h"
 
 #include "qtcompat.h"
 
 #include <QtCore/QDir>
 #include <QtCore/QFile>
+#include <QtCore/QHash>
 #include <QtCore/QUrl>
 
+#include <algorithm>
+#include <iterator>
+
 using namespace Qt::StringLiterals;
 
 enum { debugFunctionSearch = 0 };
@@ -32,9 +39,56 @@ constexpr auto briefStartElement = "<brief>"_L1;
 constexpr auto briefEndElement = "</brief>"_L1;
 constexpr auto webxmlSuffix = ".webxml"_L1;
 
-Documentation QtDocParser::retrieveModuleDocumentation()
+// Return the package of a type "PySide6.QtGui.QPainter" -> "PySide6.QtGui"
+static QStringView packageFromPythonType(QStringView pythonType)
+{
+    qsizetype pos = pythonType.startsWith(u"PySide6.") ? 8 : 0;
+    auto dot = pythonType.indexOf(u'.', pos);
+    return dot != -1 ? pythonType.sliced(0, dot) : pythonType;
+}
+
+// Return the qdoc dir "PySide6.QtGui.QPainter" -> "qtgui/webxml" (QTBUG-119500)
+static QString qdocModuleDirFromPackage(QStringView package)
+{
+    if (package.startsWith(u"PySide6."))
+        package = package.sliced(8);
+    return package.toString().toLower() + "/webxml"_L1;
+}
+
+// Populate a cache of package to WebXML dir
+static QHash<QString, QString> getPackageToModuleDir()
 {
-    return retrieveModuleDocumentation(packageName());
+    QHash<QString, QString> result;
+    const auto &typeSystemEntries = TypeDatabase::instance()->typeSystemEntries();
+    for (const auto &te : typeSystemEntries) {
+        const QString &package = te->name();
+        const QString &docPackage = te->hasDocTargetLangPackage()
+                                    ? te->docTargetLangPackage() : package;
+        result.insert(package, qdocModuleDirFromPackage(docPackage));
+    }
+    return result;
+}
+
+QString QtDocParser::qdocModuleDir(const QString &pythonType)
+{
+    static const QHash<QString, QString> packageToModuleDir = getPackageToModuleDir();
+
+    const QStringView package = packageFromPythonType(pythonType);
+    const auto it = packageToModuleDir.constFind(package);
+    if (it == packageToModuleDir.cend()) {
+        const QString known = packageToModuleDir.keys().join(", "_L1);
+        qCWarning(lcShibokenDoc, "Type from unknown package: \"%s\" (known: %s).",
+                  qPrintable(pythonType), qPrintable(known));
+        return qdocModuleDirFromPackage(package);
+    }
+    return it.value();
+}
+
+static QString xmlFileBaseName(const AbstractMetaClassPtr &metaClass)
+{
+    QString className = metaClass->qualifiedCppName().toLower();
+    className.replace("::"_L1, "-"_L1);
+    return className;
 }
 
 static void formatPreQualifications(QTextStream &str, const AbstractMetaType &type)
@@ -63,7 +117,7 @@ static void formatFunctionUnqualifiedArgTypeQuery(QTextStream &str,
         const auto flagsEntry = std::static_pointer_cast<const FlagsTypeEntry>(metaType.typeEntry());
         QString name = flagsEntry->qualifiedCppName();
         if (name.endsWith(u'>') && name.startsWith(u"QFlags<")) {
-            const int lastColon = name.lastIndexOf(u':');
+            const auto lastColon = name.lastIndexOf(u':');
             if (lastColon != -1) {
                 name.replace(lastColon + 1, name.size() - lastColon - 1, metaType.name());
                 name.remove(0, 7);
@@ -76,7 +130,7 @@ static void formatFunctionUnqualifiedArgTypeQuery(QTextStream &str,
         break;
     case AbstractMetaType::ContainerPattern: { // QVector<int>
         str << metaType.typeEntry()->qualifiedCppName() << '<';
-        const auto instantiations = metaType.instantiations();
+        const auto &instantiations = metaType.instantiations();
         for (qsizetype i = 0, size = instantiations.size(); i < size; ++i) {
             if (i)
                 str << ", ";
@@ -103,28 +157,33 @@ static QString formatFunctionArgTypeQuery(const AbstractMetaType &metaType)
     return result;
 }
 
-QString QtDocParser::functionDocumentation(const QString &sourceFileName,
-                                           const ClassDocumentation &classDocumentation,
-                                           const AbstractMetaClassCPtr &metaClass,
-                                           const AbstractMetaFunctionCPtr &func,
-                                           QString *errorMessage)
+QtDocParser::FunctionDocumentationOpt
+    QtDocParser::functionDocumentation(const QString &sourceFileName,
+                                       const ClassDocumentation &classDocumentation,
+                                       const AbstractMetaClassCPtr &metaClass,
+                                       const AbstractMetaFunctionCPtr &func, QString *errorMessage)
 {
     errorMessage->clear();
 
-    const QString docString =
-        queryFunctionDocumentation(sourceFileName, classDocumentation, metaClass,
-                                   func, errorMessage);
+    FunctionDocumentationOpt orig = queryFunctionDocumentation(sourceFileName, classDocumentation, metaClass,
+                                                               func, errorMessage);
+    if (!orig.has_value() || orig.value().description.isEmpty())
+        return orig;
 
     const auto funcModifs = DocParser::getXpathDocModifications(func, metaClass);
-    return docString.isEmpty() || funcModifs.isEmpty()
-        ? docString : applyDocModifications(funcModifs, docString);
+    if (funcModifs.isEmpty())
+        return orig;
+
+    FunctionDocumentation modified = orig.value();
+    modified.description = applyDocModifications(funcModifs, orig->description);
+    return modified;
 }
 
-QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
-                                                const ClassDocumentation &classDocumentation,
-                                                const AbstractMetaClassCPtr &metaClass,
-                                                const AbstractMetaFunctionCPtr &func,
-                                                QString *errorMessage)
+QtDocParser::FunctionDocumentationOpt
+    QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
+                                            const ClassDocumentation &classDocumentation,
+                                            const AbstractMetaClassCPtr &metaClass,
+                                            const AbstractMetaFunctionCPtr &func, QString *errorMessage)
 {
     // Search candidates by name and const-ness
     FunctionDocumentationList candidates =
@@ -132,7 +191,7 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
     if (candidates.isEmpty()) {
         *errorMessage = msgCannotFindDocumentation(sourceFileName, func.get())
                         + u" (no matches)"_s;
-        return {};
+        return std::nullopt;
     }
 
     // Try an exact query
@@ -165,7 +224,7 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
     }
 
     if (index != -1)
-        return candidates.at(index).description;
+        return candidates.at(index);
 
     // Fallback: Try matching by argument count
     const auto parameterCount = func->arguments().size();
@@ -178,12 +237,12 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
         QTextStream(errorMessage) << msgFallbackForDocumentation(sourceFileName, func.get())
             << "\n  Falling back to \"" << match.signature
             << "\" obtained by matching the argument count only.";
-        return candidates.constFirst().description;
+        return candidates.constFirst();
     }
 
     QTextStream(errorMessage) << msgCannotFindDocumentation(sourceFileName, func.get())
         << " (" << candidates.size() << " candidates matching the argument count)";
-    return {};
+    return std::nullopt;
 }
 
 // Extract the <brief> section from a WebXML (class) documentation and remove it
@@ -205,15 +264,35 @@ static QString extractBrief(QString *value)
     return briefValue;
 }
 
+// Apply the documentation parsed from WebXML to a AbstractMetaFunction and complete argument
+// names missing from parsed headers using the WebXML names (exact match only).
+static void applyDocumentation(const FunctionDocumentation &funcDoc,
+                               const QString &sourceFileName,
+                               const AbstractMetaFunctionPtr &func)
+{
+    const Documentation documentation(funcDoc.description, {}, sourceFileName);
+    func->setDocumentation(documentation);
+
+    if (const auto argCount = func->arguments().size(); argCount == funcDoc.parameterNames.size()) {
+        for (qsizetype a = 0; a < argCount; ++a) {
+            if (!func->arguments().at(a).hasName() && !funcDoc.parameterNames.at(a).isEmpty())
+                func->setArgumentName(a, funcDoc.parameterNames.at(a));
+        }
+    }
+}
+
 // Find the webxml file for global functions/enums
 // by the doc-file typesystem attribute or via include file.
 static QString findGlobalWebXmLFile(const QString &documentationDataDirectory,
+                                    const QString &package,
                                     const QString &docFile,
                                     const Include &include)
 {
     QString result;
+    const QString root = documentationDataDirectory + u'/'
+                         + QtDocParser::qdocModuleDir(package) + u'/';
     if (!docFile.isEmpty()) {
-        result = documentationDataDirectory + u'/' + docFile;
+        result = root + docFile;
         if (!result.endsWith(webxmlSuffix))
             result += webxmlSuffix;
         return QFileInfo::exists(result) ? result : QString{};
@@ -221,8 +300,7 @@ static QString findGlobalWebXmLFile(const QString &documentationDataDirectory,
     if (include.name().isEmpty())
         return {};
     // qdoc "\headerfile <QtLogging>" directive produces "qtlogging.webxml"
-    result = documentationDataDirectory + u'/' +
-             QFileInfo(include.name()).baseName() + webxmlSuffix;
+    result = root + QFileInfo(include.name()).baseName() + webxmlSuffix;
     if (QFileInfo::exists(result))
         return result;
     // qdoc "\headerfile <qdrawutil.h>" produces "qdrawutil-h.webxml"
@@ -237,49 +315,49 @@ void  QtDocParser::fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr
         return;
 
     const QString sourceFileName =
-        findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+        findGlobalWebXmLFile(documentationDataDirectory(), te->targetLangPackage(), te->docFile(), te->include());
     if (sourceFileName.isEmpty())
         return;
 
     QString errorMessage;
-    auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+    auto classDocumentationO = parseWebXml({sourceFileName}, &errorMessage);
     if (!classDocumentationO.has_value()) {
         qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
         return;
     }
-    const QString detailed =
-        functionDocumentation(sourceFileName, classDocumentationO.value(),
-                              {}, f, &errorMessage);
-    if (!errorMessage.isEmpty())
+
+    const auto funcDocOpt = functionDocumentation(sourceFileName, classDocumentationO.value(),
+                                                  {}, f, &errorMessage);
+    if (funcDocOpt.has_value())
+        applyDocumentation(funcDocOpt.value(), sourceFileName, f);
+    else if (!errorMessage.isEmpty())
         qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
-    const Documentation documentation(detailed, {});
-    f->setDocumentation(documentation);
 }
 
 void QtDocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &e)
 {
     auto te = e.typeEntry();
     const QString sourceFileName =
-        findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+        findGlobalWebXmLFile(documentationDataDirectory(), te->targetLangPackage(), te->docFile(), te->include());
     if (sourceFileName.isEmpty())
         return;
 
     QString errorMessage;
-    auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+    auto classDocumentationO = parseWebXml({sourceFileName}, &errorMessage);
     if (!classDocumentationO.has_value()) {
         qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
         return;
     }
-    if (!extractEnumDocumentation(classDocumentationO.value(), e)) {
+    if (!extractEnumDocumentation(classDocumentationO.value(), sourceFileName, e)) {
         qCWarning(lcShibokenDoc, "%s",
                   qPrintable(msgCannotFindDocumentation(sourceFileName, {}, e, {})));
     }
 }
 
-void QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
+QString QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
 {
     if (!metaClass)
-        return;
+        return {};
 
     auto context = metaClass->enclosingClass();
     while (context) {
@@ -288,32 +366,37 @@ void QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
         context = context->enclosingClass();
     }
 
-    QString sourceFileRoot = documentationDataDirectory() + u'/'
-        + metaClass->qualifiedCppName().toLower();
-    sourceFileRoot.replace(u"::"_s, u"-"_s);
-
-    QFileInfo sourceFile(sourceFileRoot + webxmlSuffix);
-    if (!sourceFile.exists())
-        sourceFile.setFile(sourceFileRoot + ".xml"_L1);
-   if (!sourceFile.exists()) {
-        qCWarning(lcShibokenDoc).noquote().nospace()
-            << "Can't find qdoc file for class " << metaClass->name() << ", tried: "
-            << QDir::toNativeSeparators(sourceFile.absoluteFilePath());
-        return;
+    // Find qdoc files of a class.
+    QStringList allCandidates;
+    const auto typeEntry = metaClass->typeEntry();
+    const QString docDir = documentationDataDirectory() + u'/'
+                           + QtDocParser::qdocModuleDir(typeEntry->targetLangPackage()) + u'/';
+    const QString baseName = xmlFileBaseName(metaClass);
+    allCandidates.append(docDir + baseName + webxmlSuffix);
+    const QString &docFile = typeEntry->docFile();
+    if (!docFile.isEmpty())
+        allCandidates.append(docDir + docFile + webxmlSuffix);
+    allCandidates.append(docDir + baseName + ".xml"_L1);
+    QStringList candidates;
+    std::copy_if(allCandidates.cbegin(), allCandidates.cend(), std::back_inserter(candidates),
+                 qOverload<const QString &>(QFileInfo::exists));
+
+   if (candidates.isEmpty()) {
+       qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindQDocFile(metaClass, allCandidates)));
+       return {};
     }
 
-    const QString sourceFileName = sourceFile.absoluteFilePath();
     QString errorMessage;
-
-    const auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+    const auto classDocumentationO = parseWebXml(candidates, &errorMessage);
     if (!classDocumentationO.has_value()) {
         qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
-        return;
+        return {};
     }
 
     const auto &classDocumentation = classDocumentationO.value();
+    const QString &sourceFileName = candidates.constFirst();
     for (const auto &p : classDocumentation.properties) {
-        Documentation doc(p.description, p.brief);
+        Documentation doc(p.description, p.brief, sourceFileName);
         metaClass->setPropertyDocumentation(p.name, doc);
     }
 
@@ -328,21 +411,23 @@ void QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
     const QString brief = extractBrief(&docString);
 
     Documentation doc;
+    doc.setSourceFile(sourceFileName);
     if (!brief.isEmpty())
-        doc.setValue(brief, Documentation::Brief);
+        doc.setValue(brief, DocumentationType::Brief);
     doc.setValue(docString);
     metaClass->setDocumentation(doc);
 
     //Functions Documentation
     const auto &funcs = DocParser::documentableFunctions(metaClass);
     for (const auto &func : funcs) {
-        const QString detailed =
-            functionDocumentation(sourceFileName, classDocumentation,
-                                  metaClass, func, &errorMessage);
-        if (!errorMessage.isEmpty())
+        const auto funcDocOpt = functionDocumentation(sourceFileName, classDocumentation,
+                                                      metaClass, func, &errorMessage);
+        if (funcDocOpt.has_value()) {
+            applyDocumentation(funcDocOpt.value(), sourceFileName,
+                               std::const_pointer_cast<AbstractMetaFunction>(func));
+        } else if (!errorMessage.isEmpty()) {
             qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
-        const Documentation documentation(detailed, {});
-        std::const_pointer_cast<AbstractMetaFunction>(func)->setDocumentation(documentation);
+        }
     }
 #if 0
     // Fields
@@ -358,17 +443,19 @@ void QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
 #endif
     // Enums
     for (AbstractMetaEnum &meta_enum : metaClass->enums()) {
-        if (!extractEnumDocumentation(classDocumentation, meta_enum)) {
+        if (!extractEnumDocumentation(classDocumentation, sourceFileName, meta_enum)) {
             qCWarning(lcShibokenDoc, "%s",
                       qPrintable(msgCannotFindDocumentation(sourceFileName, metaClass, meta_enum, {})));
         }
     }
+
+    return sourceFileName;
 }
 
 bool QtDocParser::extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+                                           const QString &sourceFileName,
                                            AbstractMetaEnum &meta_enum)
 {
-    Documentation enumDoc;
     const auto index = classDocumentation.indexOfEnum(meta_enum.name());
     if (index == -1)
         return false;
@@ -382,38 +469,34 @@ bool QtDocParser::extractEnumDocumentation(const ClassDocumentation &classDocume
             doc.insert(firstPara + 6, note);
         }
     }
-    enumDoc.setValue(doc);
+    Documentation enumDoc(doc, {}, sourceFileName);
     meta_enum.setDocumentation(enumDoc);
     return true;
 }
 
 static QString qmlReferenceLink(const QFileInfo &qmlModuleFi)
 {
-    QString result;
-    QTextStream(&result) << "<para>The module also provides <link"
-        << R"( type="page" page="https://doc.qt.io/qt-)" << QT_VERSION_MAJOR
-        << '/' << qmlModuleFi.baseName() << R"(.html")"
-        << ">QML types</link>.</para>";
-    return result;
+    return "https://doc.qt.io/qt-"_L1 + QString::number(QT_VERSION_MAJOR)
+        + u'/' + qmlModuleFi.baseName() + ".html"_L1;
 }
 
-Documentation QtDocParser::retrieveModuleDocumentation(const QString& name)
+ModuleDocumentation QtDocParser::retrieveModuleDocumentation(const QString &name)
 {
     // TODO: This method of acquiring the module name supposes that the target language uses
     // dots as module separators in package names. Improve this.
-    QString moduleName = name;
-    moduleName.remove(0, name.lastIndexOf(u'.') + 1);
-    if (moduleName == u"QtQuickControls2")
-        moduleName.chop(1);
-    const QString prefix = documentationDataDirectory() + u'/'
-        + moduleName.toLower();
-
-    const QString sourceFile = prefix + u"-index.webxml"_s;
+    QString completeModuleName = name;
+    if (completeModuleName.endsWith("QtQuickControls2"_L1))
+        completeModuleName.chop(1);
+    const QString moduleName = completeModuleName.sliced(name.lastIndexOf(u'.') + 1);
+    const QString lowerModuleName = moduleName.toLower();
+
+    const QString dirPath = documentationDataDirectory() + u'/' + qdocModuleDir(completeModuleName);
+    const QString sourceFile = dirPath + u'/' + lowerModuleName + "-index.webxml"_L1;
     if (!QFile::exists(sourceFile)) {
         qCWarning(lcShibokenDoc).noquote().nospace()
             << "Can't find qdoc file for module " <<  name << ", tried: "
             << QDir::toNativeSeparators(sourceFile);
-        return Documentation();
+        return {};
     }
 
     QString errorMessage;
@@ -422,24 +505,20 @@ Documentation QtDocParser::retrieveModuleDocumentation(const QString& name)
         qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
         return {};
     }
-
-    Documentation doc(docString, {});
-    if (doc.isEmpty()) {
+    if (docString.isEmpty()) {
         qCWarning(lcShibokenDoc, "%s",
                   qPrintable(msgCannotFindDocumentation(sourceFile, "module", name)));
-        return doc;
+        return {};
     }
 
-    // If a QML module info file exists, insert a link to the Qt docs.
-    const QFileInfo qmlModuleFi(prefix + u"-qmlmodule.webxml"_s);
-    if (qmlModuleFi.isFile()) {
-        QString docString = doc.detailed();
-        const int pos = docString.lastIndexOf(u"</description>");
-        if (pos != -1) {
-            docString.insert(pos, qmlReferenceLink(qmlModuleFi));
-            doc.setDetailed(docString);
-        }
-    }
+    ModuleDocumentation result{Documentation{docString, {}, sourceFile}, {}};
 
-    return doc;
+    // If a QML module info file exists, insert a link to the Qt docs.
+    // Use a filter as some file names are irregular.
+    // Note: These files are empty; we need to point to the web docs.
+    const QFileInfoList qmlModuleFiles =
+        QDir(dirPath).entryInfoList({"*-qmlmodule.webxml"_L1}, QDir::Files);
+    if (!qmlModuleFiles.isEmpty())
+        result.qmlTypesUrl = qmlReferenceLink(qmlModuleFiles.constFirst());
+    return result;
 }
index f6ba5e47af4c0da2b1b8536129fa0f5aacaf24ba..a439c1dabe8f35a1ad465dd1d6b334f8acea0676 100644 (file)
@@ -5,36 +5,40 @@
 #define QTDOCPARSER_H
 
 #include "docparser.h"
+#include <optional>
 
 struct ClassDocumentation;
+struct FunctionDocumentation;
 
 class QtDocParser : public DocParser
 {
 public:
     QtDocParser() = default;
-    void fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
+    QString fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
     void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f) override;
     void fillGlobalEnumDocumentation(AbstractMetaEnum &e) override;
 
-    Documentation retrieveModuleDocumentation() override;
-    Documentation retrieveModuleDocumentation(const QString& name) override;
+    ModuleDocumentation retrieveModuleDocumentation(const QString &name) override;
+
+    static QString qdocModuleDir(const QString &pythonType);
 
 private:
-    static QString functionDocumentation(const QString &sourceFileName,
-                                         const ClassDocumentation &classDocumentation,
-                                         const AbstractMetaClassCPtr &metaClass,
-                                         const AbstractMetaFunctionCPtr &func,
-                                         QString *errorMessage);
-
-    static QString queryFunctionDocumentation(const QString &sourceFileName,
-                                              const ClassDocumentation &classDocumentation,
-                                              const AbstractMetaClassCPtr &metaClass,
-                                              const AbstractMetaFunctionCPtr &func,
-                                              QString *errorMessage);
+    using FunctionDocumentationOpt = std::optional<FunctionDocumentation>;
+
+    static FunctionDocumentationOpt
+         functionDocumentation(const QString &sourceFileName,
+                               const ClassDocumentation &classDocumentation,
+                               const AbstractMetaClassCPtr &metaClass,
+                               const AbstractMetaFunctionCPtr &func, QString *errorMessage);
+    static FunctionDocumentationOpt
+        queryFunctionDocumentation(const QString &sourceFileName,
+                                   const ClassDocumentation &classDocumentation,
+                                   const AbstractMetaClassCPtr &metaClass,
+                                   const AbstractMetaFunctionCPtr &func, QString *errorMessage);
     static bool extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+                                         const QString &sourceFileName,
                                          AbstractMetaEnum &meta_enum);
 
 };
 
 #endif // QTDOCPARSER_H
-
index 23066ba2145160cb291a0ad69605f0dd4c5fcfcb..cf2fa163b7e599b01601543a76c8c7456105845f 100644 (file)
@@ -103,11 +103,11 @@ void ReportHandler::setPrefix(const QString &p)
 void ReportHandler::messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &text)
 {
     // Check for file location separator added by SourceLocation
-    int fileLocationPos = text.indexOf(u":\t");
+    auto fileLocationPos = text.indexOf(u":\t");
     if (type == QtWarningMsg) {
         if (m_silent || m_reportedWarnings.contains(text))
             return;
-        if (auto db = TypeDatabase::instance()) {
+        if (auto *db = TypeDatabase::instance()) {
             const bool suppressed = fileLocationPos >= 0
                 ? db->isSuppressedWarning(QStringView{text}.mid(fileLocationPos + 2))
                 : db->isSuppressedWarning(text);
index 76c014fbbbb668a175bc7db775ba59dec16b63bb..731c0a87946666168a7a6da137fd6937b3b7dbc6 100644 (file)
@@ -63,4 +63,3 @@ declare_test(testtyperevision)
 if (NOT DISABLE_DOCSTRINGS)
     declare_test(testmodifydocumentation)
 endif()
-
index 0bb72b3c11ffa1d61d6dde0d00a3a0f8ee0adfd2..e185e91cb39ab58842e3e89432a58c27849b721d 100644 (file)
@@ -81,4 +81,3 @@ void TestContainer::testListOfValueType()
 }
 
 QTEST_APPLESS_MAIN(TestContainer)
-
index 2152d39de88515dcdd08f8ed64952d419928a476..649da174101e219e77bc7e2b0574f53d834439ab 100644 (file)
@@ -154,5 +154,3 @@ protected:
 }
 
 QTEST_APPLESS_MAIN(TestDtorInformation)
-
-
index 18eaf5774d9aac91577b70f9951b8492f774c28c..f8b6257c90b9773c5c551ff3931640c20a89584c 100644 (file)
@@ -76,4 +76,3 @@ void TestFunctionTag::testRenameGlobalFunction()
 }
 
 QTEST_APPLESS_MAIN(TestFunctionTag)
-
index 9cf2e0cc771e1b147a503fe23271540bdb1afe6a..3858ab0ce4e00763fe22ca8ed6b51afff154f7e4 100644 (file)
@@ -47,8 +47,13 @@ R"(<typesystem package="Foo">
     // cannot handle Qt resources.
     QTemporaryDir tempDir(QDir::tempPath() + u"/shiboken_testmodifydocXXXXXX"_s);
     QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
+    constexpr auto docDir = "foo/webxml/"_L1;
     constexpr auto docFileName = "a.xml"_L1;
-    QVERIFY(QFile::copy(u":/"_s + docFileName, tempDir.filePath(docFileName)));
+    {
+        QDir dir(tempDir.path());
+        QVERIFY(dir.mkpath(docDir));
+    }
+    QVERIFY(QFile::copy(u":/"_s + docFileName, tempDir.path() + u'/' + docDir + u'/' + docFileName));
 
     QtDocParser docParser;
     docParser.setDocumentationDataDirectory(tempDir.path());
index 3773e614a8184521d4b2fdb90b9308fdf3d466f7..600c1888c3ab5ab5861ee54d380aff0e5ea8d678 100644 (file)
@@ -74,4 +74,3 @@ void NamespaceTest::testNamespaceInnerClassMembers()
 }
 
 QTEST_APPLESS_MAIN(NamespaceTest)
-
index 9eef7ec4792c471147c714804e0655583e76bc97..132d8ed50b2c22f749c5627b6617798ce4c582aa 100644 (file)
@@ -87,4 +87,3 @@ void TestNumericalTypedef::testUnsignedNumericalTypedef()
 }
 
 QTEST_APPLESS_MAIN(TestNumericalTypedef)
-
index 99cced09d7eeb2a6508e3b3f7ee2ff0a927dfd8a..6c2ea12216d3ecccbc6dae4375ee8c9ac5afca66 100644 (file)
@@ -37,4 +37,3 @@ void TestPrimitiveTypeTag::testPrimitiveTypeDefaultConstructor()
 }
 
 QTEST_APPLESS_MAIN(TestPrimitiveTypeTag)
-
index f2e26162431e8d67ef76f33760a81017286a5f82..ffc9e688cc86487ba5192f48bf9fc76178d0c2ae 100644 (file)
@@ -80,5 +80,3 @@ void TestRefCountTag::testWithApiVersion()
 
 
 QTEST_APPLESS_MAIN(TestRefCountTag)
-
-
index ae85c5a86aa95caa46504e71f58b4e35f208af2e..9eff8149424560726db64283bb2f41c4fd95b2b3 100644 (file)
@@ -33,5 +33,3 @@ void TestReferenceToPointer::testReferenceToPointerArgument()
 }
 
 QTEST_APPLESS_MAIN(TestReferenceToPointer)
-
-
index 2cc82071b4f8cc2a06a29acbb3c057357ae53e19..71d9e0df4a2f3be1ada73000a6104a6cdf279a65 100644 (file)
@@ -72,5 +72,3 @@ struct A {
 }
 
 QTEST_APPLESS_MAIN(TestRemoveField)
-
-
index 17a069b5ec8ca715ce0cd360b5eb9e73a393dc1c..8557f3a7e4e1b895a9d4830e8ea3f96684becb77 100644 (file)
@@ -95,4 +95,3 @@ struct A {
 }
 
 QTEST_APPLESS_MAIN(TestRemoveOperatorMethod)
-
index f4eecff2c56129d290b79275a799d9ffc132c6b0..86584d18178eaece629d6b241e40f5c897448bdd 100644 (file)
@@ -126,4 +126,3 @@ void  TestReverseOperators::testSpaceshipOperator()
 }
 
 QTEST_APPLESS_MAIN(TestReverseOperators)
-
index ea37c62557e44a1ca622e92e9da24a55fbe0abb9..43b1b273883fdab528b45ef70fd41faa5558c174 100644 (file)
@@ -159,7 +159,7 @@ void func(List<int>* arg) {}
     AbstractMetaFunctionCList globalFuncs = builder->globalFunctions();
     QCOMPARE(globalFuncs.size(), 1);
 
-    const auto func = globalFuncs.constFirst();
+    const auto &func = globalFuncs.constFirst();
     QCOMPARE(func->minimalSignature(), u"func(List<int>*)");
     QCOMPARE(func->arguments().constFirst().type().cppSignature(),
              u"List<int> *");
@@ -184,7 +184,7 @@ void func(List<int>& arg) {}
     const auto globalFuncs = builder->globalFunctions();
     QCOMPARE(globalFuncs.size(), 1);
 
-    const auto func = globalFuncs.constFirst();
+    const auto &func = globalFuncs.constFirst();
     QCOMPARE(func->minimalSignature(), u"func(List<int>&)");
     QCOMPARE(func->arguments().constFirst().type().cppSignature(),
              u"List<int> &");
@@ -424,7 +424,7 @@ typedef Vector<int> IntVector;
 
     const auto method = vector->findFunction("method");
     QVERIFY(method);
-    QCOMPARE(method->signature(), u"method(const Vector<int> & vector)");
+    QCOMPARE(method->signature(), "method(const Vector<int> &vector)"_L1);
 
     const auto otherMethod = vector->findFunction("otherMethod");
     QVERIFY(otherMethod);
index 50cefcfe924ece46185aa737cd4740f98ebdc03f..ec1cc5e28638346df64e4bfaa90a41c11d40c244 100644 (file)
@@ -58,4 +58,3 @@ void TestTopoSort::testTopoSort()
 }
 
 QTEST_APPLESS_MAIN(TestTopoSort)
-
index 72dae8cc5f6009200c3226517d7cfbf3c602844c..2ba9efee0a68cc1ee921826929de822112a5ab1b 100644 (file)
@@ -88,5 +88,3 @@ class Bar20 {};
 }
 
 QTEST_APPLESS_MAIN(TestTypeRevision)
-
-
index 83d981b2bd1014278f787646febcb1ecf94a94dd..21f915d261c57d5f872a88c649e637e1fd8f4495 100644 (file)
@@ -24,7 +24,7 @@ TextStream::~TextStream() = default;
 
 QChar TextStream::lastChar() const
 {
-    auto s = m_str.string();
+    const auto *s = m_str.string();
     return s != nullptr && !s->isEmpty() ? *(s->crbegin()) : QChar();
 }
 
@@ -43,7 +43,7 @@ void TextStream::outdent(int n)
 qint64 TextStream::pos() const
 {
     // QTextStream::pos() only works for QIODevice, be a bit smarter
-    if (auto s = m_str.string())
+    if (const auto *s = m_str.string())
         return s->size();
     // QIODevices need to flushed to tell the correct position.
     const_cast<QTextStream &>(m_str).flush();
index 749c4baa33bd7a5a83a500bcc45358d50dc65104..8ad7ebbdf5394f5620dd180227894294c593a4c6 100644 (file)
@@ -88,7 +88,8 @@ static const PythonTypes &builtinPythonTypes()
         {u"PyObject"_s, u"true"_s, TypeSystem::CPythonType::Other},
         // shiboken-specific
         {u"PyPathLike"_s, u"Shiboken::String::checkPath"_s, TypeSystem::CPythonType::Other},
-        {u"PySequence"_s, u"Shiboken::String::checkIterable"_s, TypeSystem::CPythonType::Other},
+        {u"PySequence"_s, u"Shiboken::String::checkIterableArgument"_s,
+         TypeSystem::CPythonType::Other},
         {u"PyUnicode"_s, u"PyUnicode_Check"_s, TypeSystem::CPythonType::String},
         {u"PyTypeObject"_s, u"PyType_Check"_s, TypeSystem::CPythonType::Other},
         {u"str"_s, u"Shiboken::String::check"_s, TypeSystem::CPythonType::String},
@@ -542,6 +543,11 @@ QString TypeDatabase::loadedTypeSystemNames() const
     return result;
 }
 
+QList<TypeSystemTypeEntryCPtr> TypeDatabase::typeSystemEntries() const
+{
+    return d->m_typeSystemEntries;
+}
+
 TypeSystemTypeEntryCPtr TypeDatabasePrivate::defaultTypeSystemType() const
 {
     return m_typeSystemEntries.value(0, nullptr);
@@ -1215,7 +1221,7 @@ bool TypeDatabasePrivate::resolveSmartPointerInstantiations(const TypeDatabasePa
 {
     const auto &instantiations = context->smartPointerInstantiations;
     for (auto it = instantiations.cbegin(), end = instantiations.cend(); it != end; ++it) {
-        auto smartPointerEntry = it.key();
+        const auto &smartPointerEntry = it.key();
         const auto instantiationNames = splitTypeList(it.value());
         SmartPointerTypeEntry::Instantiations instantiations;
         instantiations.reserve(instantiationNames.size());
index d5adca324e0225dc92cb380bf749f91655674125..cf7db5163f6d2747393397fb8a0029759654d567 100644 (file)
@@ -104,6 +104,7 @@ public:
     TypeSystemTypeEntryCPtr findTypeSystemType(const QString &name) const;
     TypeSystemTypeEntryCPtr defaultTypeSystemType() const;
     QString loadedTypeSystemNames() const;
+    QList<TypeSystemTypeEntryCPtr > typeSystemEntries() const;
     QString defaultPackageName() const;
 
     TypeEntryPtr findType(const QString &name) const;
index 11d7bf641c5cce7a7d17b650acf9f18471f95caa..bcf2621d223fb21a02f4f78ae2d10f834b98eba7 100644 (file)
@@ -32,8 +32,8 @@ public:
         InvalidToken
     };
 
-    Scanner(const QString &s)
-            : m_pos(0), m_length(s.length()), m_tokenStart(-1), m_chars(s.constData())
+    Scanner(const QString &s) :
+        m_length(s.length()), m_chars(s.constData())
     {
     }
 
@@ -43,9 +43,9 @@ public:
     QString msgParseError(const QString &why) const;
 
 private:
-    int m_pos;
-    int m_length;
-    int m_tokenStart;
+    qsizetype m_pos = 0;
+    qsizetype m_length;
+    qsizetype m_tokenStart = -1;
     const QChar *m_chars;
 };
 
index 99d42b668c6957bc41a213f30307adf034c688fa..98b82cb46150feb6c2c964758c1af7f53652436f 100644 (file)
@@ -242,7 +242,7 @@ bool TypeEntry::isUniquePointer() const
 {
     if (m_d->m_type != SmartPointerType)
         return false;
-    auto *ste = static_cast<const SmartPointerTypeEntry *>(this);
+    const auto *ste = static_cast<const SmartPointerTypeEntry *>(this);
     return ste->smartPointerType() == TypeSystem::SmartPointerType::Unique;
 }
 
@@ -512,7 +512,7 @@ QString TypeEntry::targetLangEntryName() const
 {
     if (m_d->m_cachedTargetLangEntryName.isEmpty()) {
         m_d->m_cachedTargetLangEntryName = targetLangName();
-        const int lastDot = m_d->m_cachedTargetLangEntryName.lastIndexOf(u'.');
+        const auto lastDot = m_d->m_cachedTargetLangEntryName.lastIndexOf(u'.');
         if (lastDot != -1)
             m_d->m_cachedTargetLangEntryName.remove(0, lastDot + 1);
     }
@@ -669,6 +669,8 @@ public:
     QString m_subModuleOf;
     QString m_namespaceBegin;
     QString m_namespaceEnd;
+    QString m_docTargetLangPackage;
+    TypeSystem::DocMode m_docMode = TypeSystem::DocMode::Nested;
 };
 
 TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &entryName, const QVersionNumber &vr,
@@ -718,6 +720,36 @@ void TypeSystemTypeEntry::setSubModule(const QString &s)
     d->m_subModuleOf = s;
 }
 
+bool TypeSystemTypeEntry::hasDocTargetLangPackage() const
+{
+    S_D(const TypeSystemTypeEntry);
+    return !d->m_docTargetLangPackage.isEmpty();
+}
+
+QString TypeSystemTypeEntry::docTargetLangPackage() const
+{
+    S_D(const TypeSystemTypeEntry);
+    return d->m_docTargetLangPackage;
+}
+
+void TypeSystemTypeEntry::setDocTargetLangPackage(const QString &p)
+{
+    S_D(TypeSystemTypeEntry);
+    d->m_docTargetLangPackage = p;
+}
+
+TypeSystem::DocMode TypeSystemTypeEntry::docMode() const
+{
+    S_D(const TypeSystemTypeEntry);
+    return d->m_docMode;
+}
+
+void TypeSystemTypeEntry::setDocMode(TypeSystem::DocMode m)
+{
+    S_D(TypeSystemTypeEntry);
+    d->m_docMode = m;
+}
+
 const QString &TypeSystemTypeEntry::namespaceBegin() const
 {
     S_D(const TypeSystemTypeEntry);
@@ -1335,6 +1367,7 @@ public:
     QString m_defaultConstructor;
     QString m_defaultSuperclass;
     QString m_qualifiedCppName;
+    QString m_docFile;
 
     uint m_polymorphicBase : 1;
     uint m_genericClass : 1;
@@ -1767,6 +1800,18 @@ void ComplexTypeEntry::setValueTypeWithCopyConstructorOnly(bool v)
     d->m_isValueTypeWithCopyConstructorOnly = v;
 }
 
+QString ComplexTypeEntry::docFile() const
+{
+    S_D(const ComplexTypeEntry);
+    return d->m_docFile;
+}
+
+void ComplexTypeEntry::setDocFile(const QString &docFile)
+{
+    S_D(ComplexTypeEntry);
+    d->m_docFile = docFile;
+}
+
 // FIXME PYSIDE 7: Remove this and make "true" the default
 static bool parentManagementEnabled = false;
 
index 9ecbb08a184c440e0d00720738dba1f76ee50a8d..e2d86cb92bf1faef3654314d33778dd108fbc767 100644 (file)
@@ -46,6 +46,11 @@ enum DocModificationMode {
     DocModificationXPathReplace
 };
 
+enum class DocMode {
+    Nested,
+    Flat
+};
+
 enum class ExceptionHandling {
     Unspecified,
     Off,
index 2b686e997f12dd2af8b855ef34964fcfcb8bb12d..a4887ab23f8cc16117f16cc16e923bb1694e80a2 100644 (file)
@@ -9,6 +9,7 @@
 #include "containertypeentry.h"
 #include "customconversion.h"
 #include "customtypenentry.h"
+#include "documentation_enums.h"
 #include "flagstypeentry.h"
 #include "functiontypeentry.h"
 #include "namespacetypeentry.h"
@@ -88,6 +89,8 @@ constexpr auto pyiTypeAttribute = "pyi-type"_L1;
 constexpr auto overloadNumberAttribute = "overload-number"_L1;
 constexpr auto ownershipAttribute = "owner"_L1;
 constexpr auto packageAttribute = "package"_L1;
+constexpr auto docPackageAttribute = "doc-package"_L1;
+constexpr auto docModeAttribute = "doc-mode"_L1;
 constexpr auto polymorphicBaseAttribute = "polymorphic-base"_L1;
 constexpr auto positionAttribute = "position"_L1;
 constexpr auto preferredConversionAttribute = "preferred-conversion"_L1;
@@ -258,6 +261,14 @@ ENUM_LOOKUP_BEGIN(TypeSystem::PythonEnumType, Qt::CaseSensitive,
     };
 ENUM_LOOKUP_LINEAR_SEARCH
 
+ENUM_LOOKUP_BEGIN(TypeSystem::DocMode, Qt::CaseSensitive,
+                  docModeFromAttribute)
+{
+        {u"nested", TypeSystem::DocMode::Nested},
+        {u"flat", TypeSystem::DocMode::Flat},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
 ENUM_LOOKUP_BEGIN(TypeSystem::QtMetaTypeRegistration, Qt::CaseSensitive,
                   qtMetaTypeFromAttribute)
     {
@@ -279,6 +290,22 @@ ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive,
     };
 ENUM_LOOKUP_LINEAR_SEARCH
 
+ENUM_LOOKUP_BEGIN(DocumentationFormat, Qt::CaseInsensitive,
+                  documentationFormatFromAttribute)
+    {
+        {u"native", DocumentationFormat::Native},
+        {u"target",  DocumentationFormat::Target}
+    };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(DocumentationEmphasis, Qt::CaseSensitive,
+                  documentationEmphasisFromAttribute)
+    {
+        {u"none", DocumentationEmphasis::None},
+        {u"language-note", DocumentationEmphasis::LanguageNote}
+    };
+ENUM_LOOKUP_LINEAR_SEARCH
+
 ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive,
                    ownershipFromFromAttribute)
     {
@@ -1230,7 +1257,7 @@ static QString checkSignatureError(const QString& signature, const QString& tag)
                                    "and return types should not be part of the signature.")
                                    .arg(tag, signature);
     }
-    return QString();
+    return {};
 }
 
 inline TypeEntryCPtr TypeSystemParser::currentParentTypeEntry() const
@@ -1915,6 +1942,8 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader
             if (convertBoolean(attribute.value(), parentManagementAttribute, false))
                 ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ParentManagement);
             ComplexTypeEntry::setParentManagementEnabled(true);
+        }  else if (name == docFileAttribute) {
+            ctype->setDocFile(attributes->takeAt(i).value().toString());
         }
     }
 
@@ -2020,7 +2049,8 @@ bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &,
     }
 
     TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace;
-    TypeSystem::Language lang = TypeSystem::NativeCode;
+    DocumentationFormat format = DocumentationFormat::Native;
+    DocumentationEmphasis emphasis = DocumentationEmphasis::None;
     for (auto i = attributes->size() - 1; i >= 0; --i) {
         const auto name = attributes->at(i).qualifiedName();
         if (name == u"mode") {
@@ -2033,18 +2063,32 @@ bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &,
             mode = modeOpt.value();
         } else if (name == formatAttribute) {
             const auto attribute = attributes->takeAt(i);
-            const auto langOpt = languageFromAttribute(attribute.value());
-            if (!langOpt.has_value()) {
+            const auto formatOpt = documentationFormatFromAttribute(attribute.value());
+            if (!formatOpt.has_value()) {
                 m_error = msgInvalidAttributeValue(attribute);
                 return false;
             }
-            lang = langOpt.value();
+            format = formatOpt.value();
+        } else if (name == u"emphasis") {
+            const auto attribute = attributes->takeAt(i);
+            const auto emphasisOpt = documentationEmphasisFromAttribute(attribute.value());
+            if (!emphasisOpt.has_value()) {
+                m_error = msgInvalidAttributeValue(attribute);
+                return false;
+            }
+            emphasis = emphasisOpt.value();
         }
     }
 
+    if (emphasis != DocumentationEmphasis::None && mode == TypeSystem::DocModificationXPathReplace) {
+        m_error = "Emphasis is not supported for XPathReplace"_L1;
+        return false;
+    }
+
     QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature;
     DocModification mod(mode, signature);
-    mod.setFormat(lang);
+    mod.setFormat(format);
+    mod.setEmphasis(emphasis);
     if (hasFileSnippetAttributes(attributes)) {
         const auto snippetOptional = readFileSnippet(attributes);
         if (!snippetOptional.has_value())
@@ -2094,11 +2138,22 @@ TypeSystemTypeEntryPtr TypeSystemParser::parseRootElement(const ConditionalStrea
     QString subModuleOf;
     QString namespaceBegin;
     QString namespaceEnd;
+    QString docPackage;
+    std::optional<TypeSystem::DocMode> docModeOpt;
 
     for (auto i = attributes->size() - 1; i >= 0; --i) {
         const auto name = attributes->at(i).qualifiedName();
         if (name == packageAttribute) {
             m_defaultPackage = attributes->takeAt(i).value().toString();
+        } else if (name == docPackageAttribute) {
+           docPackage = attributes->takeAt(i).value().toString();
+        } else if (name == docModeAttribute) {
+            const auto attribute = attributes->takeAt(i);
+            docModeOpt = docModeFromAttribute(attribute.value());
+            if (!docModeOpt.has_value()) {
+                qCWarning(lcShiboken, "%s",
+                          qPrintable(msgInvalidAttributeValue(attribute)));
+            }
         } else if (name == defaultSuperclassAttribute) {
             m_defaultSuperclass = attributes->takeAt(i).value().toString();
         } else if (name == exceptionHandlingAttribute) {
@@ -2152,6 +2207,10 @@ TypeSystemTypeEntryPtr TypeSystemParser::parseRootElement(const ConditionalStrea
                                                   currentParentTypeEntry()));
         moduleEntry->setSubModule(subModuleOf);
     }
+    if (!docPackage.isEmpty())
+        moduleEntry->setDocTargetLangPackage(docPackage);
+    if (docModeOpt.has_value())
+        moduleEntry->setDocMode(docModeOpt.value());
     moduleEntry->setCodeGeneration(m_generate);
     moduleEntry->setSnakeCase(snakeCase);
     if (!namespaceBegin.isEmpty())
@@ -2568,7 +2627,7 @@ bool TypeSystemParser::parseModifyField(const ConditionalStreamReader &,
 static bool parseOverloadNumber(const QXmlStreamAttribute &attribute, int *overloadNumber,
                                 QString *errorMessage)
 {
-    bool ok;
+    bool ok{};
     *overloadNumber = attribute.value().toInt(&ok);
     if (!ok || *overloadNumber < 0) {
         *errorMessage = msgInvalidAttributeValue(attribute);
@@ -3028,7 +3087,7 @@ bool TypeSystemParser::readCodeSnippet(QXmlStreamAttributes *attributes, CodeSni
     const auto snippetOptional = readFileSnippet(attributes);
     if (!snippetOptional.has_value())
         return false;
-    const auto snippet = snippetOptional.value();
+    const auto &snippet = snippetOptional.value();
 
     QString source = snippet.fileName;
     if (!snippet.snippetLabel.isEmpty())
index 9b9670696a73a7eba66ad12e1227b61f23d411ee..c7dfc735435e314f7c47bde7fa9cb4e15bdebb67 100644 (file)
@@ -27,6 +27,12 @@ public:
     QString subModuleOf() const;
     void setSubModule(const QString &);
 
+    bool hasDocTargetLangPackage() const;
+    QString docTargetLangPackage() const;
+    void setDocTargetLangPackage(const QString &p);
+    TypeSystem::DocMode docMode() const;
+    void setDocMode(TypeSystem::DocMode m);
+
     const QString &namespaceBegin() const;
     void setNamespaceBegin(const QString &n);
 
index ccacd4ce77151a88f3980a92df1afad8f3da98ab..104cca1fe0613139c0ec3b10b37c3ff3e99d4481 100644 (file)
@@ -26,8 +26,9 @@ std::shared_ptr<XQuery> XQuery::create(const QString &focus, QString *errorMessa
 #if defined(HAVE_LIBXSLT)
     return libXml_createXQuery(focus, errorMessage);
 #else
+    Q_UNUSED(focus);
     *errorMessage = QLatin1StringView(__FUNCTION__) + u" is not implemented."_s;
-    return std::shared_ptr<XQuery>();
+    return {};
 #endif
 }
 
@@ -36,6 +37,7 @@ QString xsl_transform(const QString &xml, const QString &xsl, QString *errorMess
 #if defined(HAVE_LIBXSLT)
     return libXslt_transform(xml, xsl, errorMessage);
 #else
+    Q_UNUSED(xsl);
     *errorMessage = QLatin1StringView(__FUNCTION__) + u" is not implemented."_s;
     return xml;
 #endif
index 9e1bb09b34a78cef38999d5ad30d750a0b8038c8..efc2ec7cf7dde474c71b5aec15278b713b447cb3 100644 (file)
@@ -33,4 +33,3 @@ if(BUILD_TESTS)
     enable_testing()
     add_subdirectory(tests)
 endif()
-
index 9315102f74e1999577459b79cb0490b7dc51cb72..36c9a0ec3d56daca95f908f9e11cde44f6b3282e 100644 (file)
@@ -498,4 +498,3 @@ necessary.  Here is a sample; alter the names:
 
   <signature of Ty Coon>, 1 April 1990
   Ty Coon, President of Vice
-
index 9315102f74e1999577459b79cb0490b7dc51cb72..36c9a0ec3d56daca95f908f9e11cde44f6b3282e 100644 (file)
@@ -498,4 +498,3 @@ necessary.  Here is a sample; alter the names:
 
   <signature of Ty Coon>, 1 April 1990
   Ty Coon, President of Vice
-
index 8bc0661023f984f271c9cdfbbb2d97518a426a7c..9c477705bc9ab76fa4939e95a9a32e45352f7e61 100644 (file)
@@ -117,14 +117,21 @@ macro(shiboken_internal_set_python_site_packages)
     else()
         execute_process(
             COMMAND ${Python_EXECUTABLE} -c "if True:
+                import sys
                 import sysconfig
                 from os.path import sep
 
                 # /home/qt/dev/env/lib/python3.9/site-packages
-                lib_path = sysconfig.get_path('purelib')
+                if sys.platform == 'win32':
+                    lib_path = sysconfig.get_path('purelib')
+                else:
+                    lib_path = sysconfig.get_path('purelib', scheme='posix_prefix')
 
                 # /home/qt/dev/env
-                data_path = sysconfig.get_path('data')
+                if sys.platform == 'win32':
+                    data_path = sysconfig.get_path('data')
+                else:
+                    data_path = sysconfig.get_path('data', scheme='posix_prefix')
 
                 # /lib/python3.9/site-packages
                 rel_path = lib_path.replace(data_path, '')
@@ -169,8 +176,18 @@ macro(setup_clang)
 
     find_package(Clang CONFIG REQUIRED)
     # Need to explicitly handle the version check, because the Clang package doesn't.
-    if (LLVM_PACKAGE_VERSION AND LLVM_PACKAGE_VERSION VERSION_LESS "9.0")
-        message(FATAL_ERROR "You need LLVM version 9.0 or greater to build.")
+    set(REQUIRED_LLVM "18.0")
+
+    if (LLVM_PACKAGE_VERSION AND LLVM_PACKAGE_VERSION VERSION_LESS "${REQUIRED_LLVM}")
+        message(WARNING "You need LLVM version ${REQUIRED_LLVM} or greater to build PySide "
+            "without issues, and ${LLVM_PACKAGE_VERSION} was found. "
+            "A lower version might case problems, specially on Windows.")
+        # Exception to enable Yocto builds (Kirkstone) - 6.8.x
+        set(REQUIRED_LLVM "14.0")
+        if (LLVM_PACKAGE_VERSION AND LLVM_PACKAGE_VERSION VERSION_LESS "${REQUIRED_LLVM}")
+            message(FATAL_ERROR "Using a LLVM version ${REQUIRED_LLVM} is the minimum allowed "
+                "to work pyside in some systems, however ${LLVM_PACKAGE_VERSION} was found.")
+        endif()
     endif()
 
     # CLANG_LIBRARY is read out from the cmake cache to deploy libclang
@@ -202,12 +219,6 @@ macro(get_python_extension_suffix)
     # Python_SOABI is only set by CMake 3.17+
     # TODO: Lower this to CMake 3.16 if possible.
     if(SHIBOKEN_IS_CROSS_BUILD)
-        # For android platform armv7a FindPython module return Python_SOABI as empty because
-        # it is unable to set Python_CONFIG i.e. find `python3-config` script
-        # This workaround sets the Python_SOABI manually for this platform.
-        if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a")
-            set(Python_SOABI "cpython-311}")
-        endif()
         if(NOT Python_SOABI)
             message(FATAL_ERROR "Python_SOABI variable is empty.")
         endif()
@@ -320,6 +331,17 @@ macro(shiboken_find_required_python)
             "${_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}")
         set(CMAKE_FIND_ROOT_PATH
             "${_shiboken_backup_CMAKE_FIND_ROOT_PATH}")
+
+        # For Android platform sometimes the FindPython module returns Python_SOABI as empty in
+        # certain scenarios eg: armv7a target, macOS host etc. This is because
+        # it is unable to set Python_CONFIG i.e. `python3-config` script
+        # This workaround sets the Python_SOABI manually for this Android platform.
+        # This needs to be updated manually if the Python version for Android cross compilation
+        # changes.
+        # TODO: Find a better way to set Python_SOABI for Android platform
+        if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND NOT Python_SOABI)
+            set(Python_SOABI "cpython-311")
+        endif()
     else()
         find_package(
             Python
@@ -336,9 +358,9 @@ macro(shiboken_find_required_python)
 endmacro()
 
 macro(shiboken_validate_python_version)
-    if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "7")
+    if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "8")
             message(FATAL_ERROR
-                   "Shiboken requires Python 3.7+.")
+                   "Shiboken requires Python 3.8+.")
     endif()
 endmacro()
 
@@ -877,7 +899,7 @@ function(shiboken_get_debug_level out_var)
     set(debug_level "")
     if(SHIBOKEN_DEBUG_LEVEL)
         set(debug_level "--debug-level=${SHIBOKEN_DEBUG_LEVEL}")
-    elseif(DEFINED $ENV{SHIBOKEN_DEBUG_LEVEL})
+    elseif(DEFINED ENV{SHIBOKEN_DEBUG_LEVEL})
         set(debug_level "--debug-level=$ENV{SHIBOKEN_DEBUG_LEVEL}")
     endif()
     set(${out_var} "${debug_level}" PARENT_SCOPE)
index b3f99401732cbe85ad99b891397c23d6ceeaa09c..cfc4035ab2ba6ca084fbc1520e49e74269b7ac4d 100644 (file)
@@ -44,4 +44,3 @@ if(COMMAND _python_get_config_var)
         message(STATUS "qfp:python_info:config_dir: ${_${_PYTHON_PREFIX}_CONFIGDIR}")
     endif()
 endif()
-
index 7cc6b6ffc2da4f84aaf9bf55ccdfe697d2044df5..fbb78ffafc2a705c4017e6ef23f2304ac3bbbe8f 100644 (file)
@@ -36,4 +36,3 @@ message(STATUS "qfp:qt_info:QT_INSTALL_QML: ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_
 message(STATUS "qfp:qt_info:QT_INSTALL_TESTS: ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_TESTS}")
 message(STATUS "qfp:qt_info:QT_INSTALL_TRANSLATIONS: ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_TRANSLATIONS}")
 message(STATUS "qfp:qt_info:is_shared: ${QT6_IS_SHARED_LIBS_BUILD}")
-
index 679eefe6a4da096ed4d7f9d086fd5ca2b753264e..45e04c9c73579086c389800f8da3ed0a120d0fe3 100644 (file)
@@ -57,4 +57,3 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/ShibokenHelpers.cmake"
 
 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/shiboken6${shiboken6_SUFFIX}.pc"
         DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
-
index f5a61f1394907a7b2ee98aac6137869fb9e14131..03738a9df62cd04740345cf068694e750472963c 100644 (file)
@@ -73,4 +73,3 @@ Directory where library source code is located
 
 .SH AUTHORS
 Lauro Moura <lauro.neto at openbossa dot org>, Bruno Araujo <bruno.araujo at openbossa dot org>, Hugo Lima <hugo.lima at openbossa dot org>
-
index f5a61f1394907a7b2ee98aac6137869fb9e14131..03738a9df62cd04740345cf068694e750472963c 100644 (file)
@@ -73,4 +73,3 @@ Directory where library source code is located
 
 .SH AUTHORS
 Lauro Moura <lauro.neto at openbossa dot org>, Bruno Araujo <bruno.araujo at openbossa dot org>, Hugo Lima <hugo.lima at openbossa dot org>
-
index a566f4356aa0d8fdff43cfbaaed12358d1f8e430..686fea8e5ea11c8d24db5ccf881ff472b53ddee6 100644 (file)
@@ -10,4 +10,3 @@ Requires: apiextractor
 Version: @generatorrunner_VERSION@
 Libs: -L${libdir} -lgenrunner@generator_SUFFIX@
 Cflags: -I${includedir}@generator_SUFFIX@
-
index 08c4646c62bd6fdf05aa071cf55a25741c42f143..f6b873472623996b54622d8661c71fcb00624f48 100644 (file)
@@ -98,3 +98,15 @@ dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.
   font-size: +1rem;
   color: var(--color-brand-primary);
 }
+
+article[role="main"] section[id^="pyside6-"] h1 {
+    font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt em span.pre{
+    font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt span.pre {
+    font-size: 1.5em;
+}
index e3575bc061a510717f77688170c31a2c260cd06d..906cf9b862112d3270e61d689311af5ea85d40c2 100644 (file)
@@ -15,9 +15,9 @@ C++ examples
     .. grid-item-card:: Scriptable Application
         :class-item: cover-img
         :link: ../../examples/example_scriptableapplication_scriptableapplication.html
-        :img-top: ../../../_images/example_no_image.png
+        :img-top: ../../../pyside6/doc/example_no_image.png
 
     .. grid-item-card:: Widget Binding
         :class-item: cover-img
         :link: ../../examples/example_widgetbinding_widgetbinding.html
-        :img-top: ../../../_images/example_no_image.png
+        :img-top: ../../../pyside6/doc/example_no_image.png
index 7507896984e291db9a2f17c34ef0c03d9fbfb58f..88befaa2d61ae9c5c407b1783d63414deabfe95a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import fileinput
 import re
index 14340ab69d217be0d25dbaf9ccd39c53ba99456d..31df11b812ed73d6d125514bf55d2d6478c125d8 100644 (file)
@@ -60,7 +60,7 @@ the typesystem and, in most cases, the injected code.
     well to prevent the headers from being suppressed in the generated
     code.
 
-::ref:`typesystem`: XML files that provides the developer with a tool to customize the
+::ref:`typesystem_details`: XML files that provides the developer with a tool to customize the
              way that the generators will see the classes and functions. For
              example, functions can be renamed, have its signature changed and
              many other actions.
@@ -390,4 +390,3 @@ becomes
 .. code-block:: ini
 
      VALUE-ARGUMENT = VALUE
-
index 2f1c6d166ca7208275cb327222f450fa43a38444..2bb18dad5f19088ba8b3c8683ea5177e236364e5 100644 (file)
-.. module:: Shiboken
+.. py:module:: Shiboken
 
-.. |maya| unicode:: Maya U+2122
+    .. |maya| unicode:: Maya U+2122
 
-.. _shiboken-module:
+    .. _shiboken-module:
 
-Shiboken module
-***************
+    Shiboken module
+    ***************
 
-Functions
-^^^^^^^^^
+    Functions
+    ^^^^^^^^^
 
-.. container:: function_list
+    .. container:: function_list
 
-    *    def :meth:`isValid<shiboken.isValid>` (obj)
-    *    def :meth:`wrapInstance<shiboken.wrapInstance>` (address, type)
-    *    def :meth:`getCppPointer<shiboken.getCppPointer>` (obj)
-    *    def :meth:`delete<shiboken.delete>` (obj)
-    *    def :meth:`isOwnedByPython<shiboken.isOwnedByPython>` (obj)
-    *    def :meth:`wasCreatedByPython<shiboken.wasCreatedByPython>` (obj)
-    *    def :meth:`dump<shiboken.dump>` (obj)
-    *    def :meth:`disassembleFrame<shiboken.disassembleFrame>` (marker)
+        *    :py:func:`isValid`
+        *    :py:func:`wrapInstance`
+        *    :py:func:`getCppPointer`
+        *    :py:func:`delete`
+        *    :py:func:`ownedByPython`
+        *    :py:func:`createdByPython`
+        *    :py:func:`dump`
+        *    :py:func:`disassembleFrame`
+        *    :py:func:`dumpTypeGraph`
+        *    :py:func:`dumpWrapperMap`
+        *    :py:func:`dumpConverters`
 
-Detailed description
-^^^^^^^^^^^^^^^^^^^^
+    Classes
+    ^^^^^^^
 
-This Python module can be used to access internal information related to our
-binding technology. Access to this internal information is required to e.g.:
-integrate PySide with Qt based programs that offer Python scripting like |maya|
-or just for debug purposes.
+    .. container:: class_list
 
-Some function description refer to "Shiboken based objects", wich means
-Python objects instances of any Python Type created using Shiboken.
+        *    :py:class:`VoidPtr`
 
-To import the module:
+    Detailed description
+    ^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: python
+    This Python module can be used to access internal information related to our
+    binding technology. Access to this internal information is required to e.g.:
+    integrate PySide with Qt based programs that offer Python scripting like |maya|
+    or just for debug purposes.
 
-    from shiboken6 import Shiboken
+    Some function description refer to "Shiboken based objects", which means
+    Python objects instances of any Python Type created using Shiboken.
 
-.. function:: isValid(obj)
+    To import the module:
 
-    Given a Python object, returns True if the object methods can be called
-    without an exception being thrown. A Python wrapper becomes invalid when
-    the underlying C++ object is destroyed or unreachable.
+    .. code-block:: python
 
-.. function:: wrapInstance(address, type)
+        from shiboken6 import Shiboken
 
-    Creates a Python wrapper for a C++ object instantiated at a given memory
-    address - the returned object type will be the same given by the user.
+    .. py:function:: isValid(obj: object) -> bool
 
-    The type must be a Shiboken type, the C++ object will not be
-    destroyed when the returned Python object reach zero references.
+        Given a Python object, returns True if the object methods can be called
+        without an exception being thrown. A Python wrapper becomes invalid when
+        the underlying C++ object is destroyed or unreachable.
 
-    If the address is invalid or doesn't point to a C++ object of given type
-    the behavior is undefined.
+        :param obj: Python object to validate.
 
-.. function:: getCppPointer(obj)
+    .. py:function:: wrapInstance(address: int, python_type: type) -> Shiboken.object
 
-    Returns a tuple of longs that contain the memory addresses of the
-    C++ instances wrapped by the given object.
+        Creates a Python wrapper for a C++ object instantiated at a given memory
+        address - the returned object type will be the same given by the user.
 
-.. function:: delete(obj)
+        The type must be a Python type. The C++ object will not be
+        destroyed when the returned Python object reach zero references.
 
-    Deletes the C++ object wrapped by the given Python object.
+        If the address is invalid or doesn't point to a C++ object of given type
+        the behavior is undefined.
 
-.. function:: isOwnedByPython(obj)
+        :param int address: Address of the C++ object.
+        :param type python_type: Python type for the corresponding C++ object.
 
-    Given a Python object, returns True if Python is responsible for deleting
-    the underlying C++ object, False otherwise.
+    .. py:function:: getCppPointer(obj: Shiboken.object) -> tuple[int, ...]
 
-    If the object was not a Shiboken based object, a TypeError is
-    thrown.
+        Returns a tuple of longs that contain the memory addresses of the
+        C++ instances wrapped by the given object.
 
-.. function:: wasCreatedByPython(obj)
+        :param obj: Shiboken object.
 
-    Returns true if the given Python object was created by Python.
+    .. py:function:: delete(obj: Shiboken.object)
 
-.. function:: dump(obj)
+        Deletes the C++ object wrapped by the given Python object.
 
-    Returns a string with implementation-defined information about the
-    object.
-    This method should be used **only** for debug purposes by developers
-    creating their own bindings as no guarantee is provided that
-    the string format will be the same across different versions.
+        :param obj: Shiboken object.
 
-    If the object is not a Shiboken based object, a message is printed.
+    .. py:function:: ownedByPython(obj: Shiboken.object) -> bool
 
-.. function:: disassembleFrame(label)
+        Given a Shiboken object, returns True if Python is responsible for deleting
+        the underlying C++ object, False otherwise.
 
-    Prints the current executing Python frame to stdout and flushes.
-    The disassembly is decorated by some label. Example:
+        If the object was not a Shiboken based object, a TypeError is
+        thrown.
 
-    .. code-block:: python
+        :param obj: Shiboken object.
+
+    .. py:function:: createdByPython(obj: Shiboken.object) -> bool
+
+        Returns true if the given Python object was created by Python.
+
+        :param obj: Shiboken object.
+
+    .. py:function:: dump(obj: object) -> str
+
+        Returns a string with implementation-defined information about the
+        object.
+        This method should be used **only** for debug purposes by developers
+        creating their own bindings as no guarantee is provided that
+        the string format will be the same across different versions.
+
+        If the object is not a Shiboken based object, a message is printed.
+
+        :param obj: Python object.
+
+    .. py:function:: disassembleFrame(label: str)
+
+        Prints the current executing Python frame to stdout and flushes.
+        The disassembly is decorated by some label. Example:
+
+        .. code-block:: python
+
+            lambda: 42
+
+        is shown from inside C++ as
+
+        .. code-block:: c
+
+            <label> BEGIN
+            1           0 LOAD_CONST               1 (42)
+                        2 RETURN_VALUE
+            <label> END
 
-        lambda: 42
+        When you want to set a breakpoint at the `disassembleFrame` function
+        and you use it from C++, you use the pure function name.
 
-    is shown from inside C++ as
+        When you want to use it from Python, you can insert it into your Python
+        code and then maybe instead set a breakpoint at `SbkShibokenModule_disassembleFrame`
+        which is the generated wrapper.
 
-    .. code-block:: c
+        `label` is a simple string in C++. In Python, you can use any object;
+        internally the `str` function is called with it.
 
-        <label> BEGIN
-          1           0 LOAD_CONST               1 (42)
-                      2 RETURN_VALUE
-        <label> END
+        This method should be used **only** for debug purposes by developers.
 
-    When you want to set a breakpoint at the `disassembleFrame` function
-    and you use it from C++, you use the pure function name.
+        :param label: Python string.
 
-    When you want to use it from Python, you can insert it into your Python
-    code and then maybe instead set a breakpoint at `SbkShibokenModule_disassembleFrame`
-    which is the generated wrapper.
+    .. py:function:: dumpTypeGraph(file_name: str) -> bool
 
-    `label` is a simple string in C++. In Python, you can use any object;
-    internally the `str` function is called with it.
+        Dumps the inheritance graph of the types existing in libshiboken
+        to ``.dot`` file for use with `Graphviz <https://graphviz.org/>`_.
 
-    This method should be used **only** for debug purposes by developers.
+        :param file_name: Name of the file to write the graph.
 
- .. function:: dumpTypeGraph(file_name)
+    .. py:function:: dumpWrapperMap()
 
-    Dumps the inheritance graph of the types existing in libshiboken
-    to ``.dot`` file for use with `Graphviz <https://graphviz.org/>`_.
+        Dumps the map of wrappers existing in libshiboken to standard error.
 
-.. function:: dumpWrapperMap()
+    .. py:function:: dumpConverters()
 
-    Dumps the map of wrappers existing in libshiboken to standard error.
+        Dumps the map of named converters existing in libshiboken to standard
+        error.
 
- .. py:class:: VoidPtr(address, size = -1, writeable = 0)
+    .. py:class:: VoidPtr(address, size = -1, writeable = 0)
 
-     :param address: (PyBuffer, SbkObject, int, VoidPtr)
-     :param size: int
-     :param writeable: int
+        :param address: (PyBuffer, SbkObject, int, VoidPtr)
+        :param size: int
+        :param writeable: int
 
-     Represents a chunk of memory by address and size and implements the ``buffer`` protocol.
-     It can be constructed from a ``buffer``, a Shiboken based object, a memory address
-     or another VoidPtr instance.
+        Represents a chunk of memory by address and size and implements the ``buffer`` protocol.
+        It can be constructed from a ``buffer``, a Shiboken based object, a memory address
+        or another VoidPtr instance.
 
-     .. py:method:: toBytes()
+        .. py:method:: toBytes()
 
-         :rtype: bytes
+            :rtype: bytes
 
-         Returns the contents as ``bytes``.
+            Returns the contents as ``bytes``.
index 26f929801630c88661e2a3dd01d90fc54521473c..37a4a661b794c8c9e28fc2eca9636bc0075f1cce 100644 (file)
@@ -38,12 +38,11 @@ Modifying types
 .. toctree::
    :maxdepth: 1
 
-   typesystem_arguments.rst
+   Function argument modifications <typesystem_arguments.rst>
    typesystem_codeinjection.rst
    typesystem_converters.rst
    typesystem_containers.rst
    typesystem_templates.rst
-   typesystem_modify_function.rst
    typesystem_manipulating_objects.rst
    typesystem_conversionrule.rst
    typesystem_documentation.rst
index d950b6c3266347e5b37b42bd623aa6322976ffe9..c3ad1c7077befafcbcd6a57af4641819e78cb374 100644 (file)
@@ -1,56 +1,45 @@
-.. _modifying-arguments:
+.. _modify-argument:
 
-Modifying Arguments
--------------------
+modify-argument
+---------------
 
-.. _conversionrule-on-arguments:
-
-conversion-rule
-^^^^^^^^^^^^^^^
-
-The ``conversion-rule`` node allows you to write customized code to convert
-the given argument between the target language and C++.
-It is then a child of the :ref:`modify-argument` node:
+Function argument modifications consist of a list of ``modify-argument`` nodes
+contained in :ref:`modify-function`, :ref:`add-function` or
+:ref:`declare-function` nodes. Nested :ref:`remove-argument`,
+:ref:`replace-default-expression`, :ref:`remove-default-expression`,
+:ref:`replace-type`, :ref:`reference-count` and :ref:`define-ownership`
+nodes specify the details of the modification.
 
 .. code-block:: xml
 
-    <modify-argument index="2">
-    <!-- for the second argument of the function -->
-    <conversion-rule class="target | native">
-        // the code
-    </conversion-rule>
-    </modify-argument>
+     <modify-function>
+         <modify-argument index="return | this | 1 ..." rename="..."
+          invalidate-after-use = "true | false" pyi-type="...">
+             // modifications
+         </modify-argument>
+     </modify-function>
 
-The ``class`` attribute accepts one of the following values to define the
-conversion direction to be either ``target-to-native`` or ``native-to-target``:
+Set the ``index`` attribute to "1" for the first argument, "2" for the second
+one and so on. Alternatively, set it to "return" or "this" if you want to
+modify the function's return value or the object the function is called upon,
+respectively.
 
-* ``native``: Defines the conversion direction to be ``target-to-native``.
-              It is similar to the existing ``<target-to-native>`` element.
-              See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
-
-* ``target``: Defines the conversion direction to be ``native-to-target``.
-              It is similar to the existing ``<native-to-target>`` element.
-              See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
-
-This node is typically used in combination with the :ref:`replace-type` and
-:ref:`remove-argument` nodes. The given code is used instead of the generator's
-conversion code.
+The optional ``rename`` attribute is used to rename a argument and use this
+new name in the generated code. This attribute can be used to enable the usage
+of ``keyword arguments``.
 
-Writing %N in the code (where N is a number), will insert the name of the
-nth argument. Alternatively, %in and %out which will be replaced with the
-name of the conversion's input and output variable, respectively. Note the
-output variable must be declared explicitly, for example:
-
-.. code-block:: xml
-
-    <conversion-rule class="native">
-    bool %out = (bool) %in;
-    </conversion-rule>
+The optional ``pyi-type`` attribute specifies the type to appear in the
+signature strings and  ``.pyi`` files. The type string is determined by
+checking this attribute value, the :ref:`replace-type` modification and
+the C++ type. The attribute can be used for example to enclose
+a pointer return value within ``Optional[]`` to indicate that ``None``
+can occur.
 
-.. note::
+For the optional ``invalidate-after-use`` attribute,
+see :ref:`invalidationafteruse` .
 
-    You can also use the ``conversion-rule`` node to specify
-    :ref:`a conversion code which will be used instead of the generator's conversion code everywhere for a given type <conversion-rule-tag>`.
+Naming, type, default value modifications
++++++++++++++++++++++++++++++++++++++++++
 
 .. _remove-argument:
 
@@ -130,6 +119,9 @@ If the new type is a class, the ``modified-type`` attribute must be set to
 the fully qualified name (including name of the package as well as the class
 name).
 
+Ownership/Reference modifications
++++++++++++++++++++++++++++++++++
+
 .. _define-ownership:
 
 define-ownership
@@ -138,25 +130,26 @@ define-ownership
 The ``define-ownership`` tag indicates that the function changes the ownership
 rules of the argument object, and it is a child of the
 :ref:`modify-argument` node.
+
+.. code-block:: xml
+
+    <modify-argument>
+          <define-ownership class="target | native"
+                            owner="target | c++ | default" />
+    </modify-argument>
+
 The ``class`` attribute specifies the class of
 function where to inject the ownership altering code
 (see :ref:`codegenerationterminology`). The ``owner`` attribute
 specifies the new ownership of the object. It accepts the following values:
 
 * target: the target language will assume full ownership of the object.
-          The native resources will be deleted when the target language
-          object is finalized.
+  The native resources will be deleted when the target language
+  object is finalized.
 * c++: The native code assumes full ownership of the object. The target
-       language object will not be garbage collected.
+  language object will not be garbage collected.
 * default: The object will get default ownership, depending on how it
-           was created.
-
-.. code-block:: xml
-
-    <modify-argument>
-          <define-ownership class="target | native"
-                            owner="target | c++ | default" />
-    </modify-argument>
+  was created.
 
 .. _reference-count:
 
@@ -166,47 +159,40 @@ reference-count
 The ``reference-count`` tag dictates how an argument should be handled by the
 target language reference counting system (if there is any), it also indicates
 the kind of relationship the class owning the function being modified has with
-the argument. It is a child of the :ref:`modify-argument` node.
-For instance, in a model/view relation a view receiving a model
-as argument for a **setModel** method should increment the model's reference
-counting, since the model should be kept alive as much as the view lives.
-Remember that out hypothetical view could not become parent of the model,
-since the said model could be used by other views as well.
-The ``action`` attribute specifies what should be done to the argument
-reference counting when the modified method is called. It accepts the
-following values:
-
-* add: increments the argument reference counter.
-* add-all: increments the reference counter for each item in a collection.
-* remove: decrements the argument reference counter.
-* set: will assign the argument to the variable containing the reference.
-* ignore: does nothing with the argument reference counter
-          (sounds worthless, but could be used in situations
-           where the reference counter increase is mandatory
-           by default).
+the argument (represented as lists of referred-to objects stored in the
+owner class). It is a child of the :ref:`modify-argument` node.
 
 .. code-block:: xml
 
     <modify-argument>
-          <reference-count action="add|add-all|remove|set|ignore" variable-name="..." />
+          <reference-count action="add|remove|set|ignore" variable-name="..." />
     </modify-argument>
 
+The ``action`` attribute specifies what should be done to the argument
+reference counting when the modified method is called. It accepts the
+following values:
 
-The variable-name attribute specifies the name used for the variable that
-holds the reference(s).
-
-.. _replace-value:
-
-replace-value
-^^^^^^^^^^^^^
-
-The ``replace-value`` attribute lets you replace the return statement of a
-function with a fixed string. This attribute can only be used for the
-argument at ``index`` 0, which is always the function's return value.
+* add: Adds the argument to the list of previous argument values stored
+  under this ``variable-name`` or function signature and increments
+  the argument reference counter.
+* remove: Decrements the argument reference counter and removes it from
+  the list of  argument values stored under this ``variable-name``
+  or function signature.
+* set: Decreases the reference count of the previously stored argument values
+  under this ``variable-name`` or function signature and removes them.
+  Stores the argument and increments the argument reference counter.
+* ignore: does nothing with the argument reference counter
+  (sounds worthless, but could be used in situations
+  where the reference counter increase is mandatory by default).
 
-.. code-block:: xml
+The ``variable-name`` attribute specifies the name used for the variable that
+holds the reference(s). It defaults to the function signature.
 
-     <modify-argument index="0" replace-value="this"/>
+For instance, in a model/view relation, a view receiving a model
+as argument for a **setModel()** method should increment the model's reference
+counting, since the model should be kept alive as long as the view lives.
+Remember that our hypothetical view cannot become a :ref:`parent` of the
+model, since the said model could be used by other views as well.
 
 .. _parent:
 
@@ -227,3 +213,68 @@ It is a child of the :ref:`modify-argument` node.
 In the ``index`` argument you must specify the parent argument. The action
 *add* creates a parent link between objects, while *remove* will undo the
 parentage relationship.
+
+Other modifications
++++++++++++++++++++
+
+.. _conversionrule-on-arguments:
+
+conversion-rule
+^^^^^^^^^^^^^^^
+
+The ``conversion-rule`` node allows you to write customized code to convert
+the given argument between the target language and C++.
+It is then a child of the :ref:`modify-argument` node:
+
+.. code-block:: xml
+
+    <modify-argument index="2">
+    <!-- for the second argument of the function -->
+    <conversion-rule class="target | native">
+        // the code
+    </conversion-rule>
+    </modify-argument>
+
+The ``class`` attribute accepts one of the following values to define the
+conversion direction to be either ``target-to-native`` or ``native-to-target``:
+
+* ``native``: Defines the conversion direction to be ``target-to-native``.
+              It is similar to the existing ``<target-to-native>`` element.
+              See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
+
+* ``target``: Defines the conversion direction to be ``native-to-target``.
+              It is similar to the existing ``<native-to-target>`` element.
+              See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
+
+This node is typically used in combination with the :ref:`replace-type` and
+:ref:`remove-argument` nodes. The given code is used instead of the generator's
+conversion code.
+
+Writing %N in the code (where N is a number), will insert the name of the
+nth argument. Alternatively, %in and %out which will be replaced with the
+name of the conversion's input and output variable, respectively. Note the
+output variable must be declared explicitly, for example:
+
+.. code-block:: xml
+
+    <conversion-rule class="native">
+    bool %out = (bool) %in;
+    </conversion-rule>
+
+.. note::
+
+    You can also use the ``conversion-rule`` node to specify
+    :ref:`a conversion code which will be used instead of the generator's conversion code everywhere for a given type <conversion-rule-tag>`.
+
+.. _replace-value:
+
+replace-value
+^^^^^^^^^^^^^
+
+The ``replace-value`` attribute lets you replace the return statement of a
+function with a fixed string. This attribute can only be used for the
+argument at ``index`` 0, which is always the function's return value.
+
+.. code-block:: xml
+
+     <modify-argument index="0" replace-value="this"/>
index fb41e28e1dfae25f316327ba7402cfe591a06026..a74b779bd6e8d290320020c55fbc35140ff82bc8 100644 (file)
@@ -34,4 +34,3 @@ modification is applied (see :ref:`codeinjectionsemantics`,
 The value **Target** means the modification is applied to the
 **Python Wrapper**. The value **Native** means the modification is applied to
 the **C++ Wrapper**.
-
index b5593e20fe4f198d6f7a188bd1160a46b5b1bc37..9ce0191eeaca54466dcc38ecef367aa3745b0977 100644 (file)
@@ -281,4 +281,3 @@ opaque container.
           demonstrates an example of using an opaque container `QPointList`, which wraps a C++
           `QList<QPoint>`. The corresponding typesystem file where QPointList can be found `here
           <https://code.qt.io/cgit/pyside/pyside-setup.git/tree/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml>`_
-
index 9a8f51c18b209bb657b61811e7f1e035ae2b0fa8..cee45bfb37b25160e0d7d4deddf2ef636d484683 100644 (file)
@@ -130,4 +130,3 @@ attribute, but it isn't unusual that some special check is needed. The variables
 the generator as in the ``native-to-target`` tag.
 
 The ``file`` and ``snippet`` attributes are also supported (see :ref:`inject-code` nodes).
-
index 4e7d18b993942531068badb8cdcb2599a1dfee36..47ae54fceaa5e270bd513a561d74940698acc165 100644 (file)
@@ -12,6 +12,7 @@ documentation. This node is a child of the :ref:`object-type`,
 
      <value-type>
          <inject-documentation mode="append | prepend | replace" format="native | target"
+                               emphasis="none | language-note"
                                file="[file]" snippet="[label]">
              // the documentation
          </inject-code>
@@ -25,6 +26,10 @@ occur and it accepts the following values:
 * native: Before XML<->Backend transformation occur, so the injected code *must* be a valid XML.
 * target: After XML<->Backend transformation occur, so the injected code *must* be a valid backend format.
 
+The optional **emphasis** attribute enables emphasizing the documentation snippet.
+The value ``language-note`` encloses it in a custom Sphinx admonition entitled
+*Python Language Note*. The text should be a plain paragraph for this to work.
+
 The optional ``file`` attribute specifies the file name
 (see :ref:`external-snippets`).
 
index e024cdf0002fae3d6446b522f41673898cca159f..b63a4710ab155f39f59dd80ac85b52544fad208a 100644 (file)
@@ -154,8 +154,8 @@ modify-function
 The ``modify-function`` node allows you to modify a given C++ function when
 mapping it onto the target language, and it is a child of a :ref:`function`,
 :ref:`namespace`, :ref:`object-type` or a :ref:`value-type` node.
-Use the :ref:`modify-argument` node to specify which argument the
-modification affects.
+Nested :ref:`modify-argument` nodes can used to modify arguments
+or return values.
 
 .. code-block:: xml
 
@@ -268,7 +268,7 @@ add-function
 
 The ``add-function`` node allows you to add a given function onto the target
 language, and it is a child of an :ref:`object-type` or :ref:`value-type` nodes if the
-function is supposed to be a method, or :ref:`namespace` and :ref:`typesystem` if
+function is supposed to be a method, or :ref:`namespace` and :ref:`typesystem_details` if
 the function is supposed to be a function inside a namespace or a global function.
 It may contain :ref:`modify-argument` nodes.
 
@@ -333,7 +333,7 @@ declare-function
 The ``declare-function`` node allows you to declare a function present in
 the type and it is a child of an :ref:`object-type` or :ref:`value-type` nodes
 if the function is supposed to be a method, or :ref:`namespace` and
-:ref:`typesystem` if the function is supposed to be a function inside a
+:ref:`typesystem_details` if the function is supposed to be a function inside a
 namespace or a global function. It may contain :ref:`modify-argument` nodes.
 
 .. code-block:: xml
diff --git a/sources/shiboken6/doc/typesystem_modify_function.rst b/sources/shiboken6/doc/typesystem_modify_function.rst
deleted file mode 100644 (file)
index 54ac641..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-.. _modifying-functions:
-
-Modifying Functions
--------------------
-
-.. _modify-argument:
-
-modify-argument
-^^^^^^^^^^^^^^^
-
-Function modifications consist of a list of ``modify-argument`` nodes
-contained in :ref:`modify-function`, :ref:`add-function` or
-:ref:`declare-function` nodes. Use the :ref:`remove-argument`,
-:ref:`replace-default-expression`, :ref:`remove-default-expression`,
-:ref:`replace-type`, :ref:`reference-count` and :ref:`define-ownership`
-nodes to specify the details of the modification.
-
-.. code-block:: xml
-
-     <modify-function>
-         <modify-argument index="return | this | 1 ..." rename="..."
-          invalidate-after-use = "true | false" pyi-type="...">
-             // modifications
-         </modify-argument>
-     </modify-function>
-
-Set the ``index`` attribute to "1" for the first argument, "2" for the second
-one and so on. Alternatively, set it to "return" or "this" if you want to
-modify the function's return value or the object the function is called upon,
-respectively.
-
-The optional ``rename`` attribute is used to rename a argument and use this
-new name in the generated code. This attribute can be used to enable the usage
-of ``keyword arguments``.
-
-The optional ``pyi-type`` attribute specifies the type to appear in the
-signature strings and  ``.pyi`` files. The type string is determined by
-checking this attribute value, the :ref:`replace-type` modification and
-the C++ type. The attribute can be used for example to enclose
-a pointer return value within ``Optional[]`` to indicate that ``None``
-can occur.
-
-For the optional ``invalidate-after-use`` attribute,
-see :ref:`invalidationafteruse` .
index 705c2cd260306eb6162b2913f635dd1c46ff4070..a8c2010d1f06910bb8dda4bad663ce9b5f19eabb 100644 (file)
@@ -8,7 +8,7 @@ suppress-warning
 
 The generator will generate several warnings which may be irrelevant to the
 user. The ``suppress-warning`` node suppresses the specified warning, and it is
-a child of the :ref:`typesystem` node.
+a child of the :ref:`typesystem_details` node.
 
 .. code-block:: xml
 
@@ -27,7 +27,7 @@ extra-includes
 
 The ``extra-includes`` node contains declarations of additional include files,
 and it can be a child of the :ref:`namespace`, :ref:`value-type`,
-:ref:`object-type` and :ref:`typesystem` and nodes.
+:ref:`object-type` and :ref:`typesystem_details` and nodes.
 
 The generator automatically tries to read the global header for each type but
 sometimes it is required to include extra files in the generated C++ code to
@@ -48,7 +48,7 @@ the file is located in $INCLUDEPATH and will be included using #include <...>,
 *local* means that the file is in a local directory and will be included
 using #include "...".
 
-When specified as a child of the :ref:`typesystem` node, the include
+When specified as a child of the :ref:`typesystem_details` node, the include
 directives are added to the module source file which contains
 the type converter and registration code. It can be used to specify
 additional includes required for the converter code snippets.
index f65b79bb485cf7701ba479c97296bb6e8cbeaf94..ab085df8da8353824aa43bdd1a1942c5792c164f 100644 (file)
@@ -25,7 +25,7 @@ in the type system paths passed as command line argument.
 Note that this is not a standard externally parsed entity due to the limitations
 of the underlying parser.
 
-.. _typesystem:
+.. _typesystem_details:
 
 typesystem
 ^^^^^^^^^^
@@ -49,7 +49,9 @@ It can have a number of attributes, described below.
                 exception-handling="..."
                 snake-case="yes | no | both"
                 namespace-begin="..."
-                namespace-end="..." >
+                namespace-end="..."
+                doc-package="..."
+                doc-mode = "nested | flat" >
     </typesystem>
 
 The **package** attribute is a string describing the package to be used,
@@ -86,6 +88,18 @@ to allow for linking several versions of them together.
 For example, for *Qt*, one would specify ``QT_BEGIN_NAMESPACE``,
 ``QT_END_NAMESPACE``, respectively.
 
+The *optional* attribute **doc-package** specifies an alternate package name
+for the documentation. This is used for finding the ``qdoc`` or ``doxygen``
+files for the documentation generation. It is mainly relevant for Qt where for
+example the documentation of the ``QtMultimediaWidgets`` module is generated
+into the ``QtMultimedia`` module.
+
+The *optional* attribute **doc-mode** determines whether to nest the
+documentation of inner classes into the outer classes' pages. Its default value
+is **nested**. It is possible to turn this off by specifying **flat** in case
+too many classes are cointained in a namespace, causing excessively long pages.
+However, the referencing may then not fully work.
+
 .. _load-typesystem:
 
 load-typesystem
@@ -93,7 +107,7 @@ load-typesystem
 
 The ``load-typesystem`` node specifies which type systems to load when mapping
 multiple libraries to another language or basing one library on another, and
-it is a child of the :ref:`typesystem` node.
+it is a child of the :ref:`typesystem_details` node.
 
 .. code-block:: xml
 
@@ -116,7 +130,7 @@ rejection
 ^^^^^^^^^
 
 The ``rejection`` node rejects the given class, or the specified function
-or field, and it is a child of the :ref:`typesystem` node.
+or field, and it is a child of the :ref:`typesystem_details` node.
 
 .. code-block:: xml
 
@@ -141,7 +155,7 @@ primitive-type
 ^^^^^^^^^^^^^^
 
 The ``primitive-type`` node describes how a primitive type is mapped from C++ to
-the target language, and is a child of the :ref:`typesystem` node. It may
+the target language, and is a child of the :ref:`typesystem_details` node. It may
 contain :ref:`conversion-rule` child nodes. Note that most primitives are
 already specified in the QtCore typesystem (see :ref:`primitive-cpp-types`).
 
@@ -203,7 +217,7 @@ namespace-type
 ^^^^^^^^^^^^^^
 
 The ``namespace-type`` node maps the given C++ namespace to the target
-language, and it is a child of the :ref:`typesystem` node or other
+language, and it is a child of the :ref:`typesystem_details` node or other
 ``namespace-type`` nodes. It may contain :ref:`add-function`,
 :ref:`declare-function`, :ref:`enum-type`, :ref:`extra-includes`,
 :ref:`include-element`, :ref:`modify-function`, ``namespace-type``,
@@ -221,6 +235,7 @@ child nodes.
             since="..."
             extends = "..."
             files = "..."
+            doc-file = "..."
             revision="..." />
     </typesystem>
 
@@ -261,13 +276,16 @@ The *optional* **file** attribute specifies a regular expression matching the
 include files whose contents are to be associated with the current module in
 case of a namespace spanning several modules.
 
+The *optional* **doc-file** attribute specifies the base name of a ``.qdoc``
+file where documentation of the class is to be found (``qdoc`` only).
+
 .. _enum-type:
 
 enum-type
 ^^^^^^^^^
 
 The ``enum-type`` node maps the given enum from C++ to the target language,
-and it is a child of the :ref:`typesystem` node. Use
+and it is a child of the :ref:`typesystem_details` node. Use
 :ref:`reject-enum-value` child nodes to reject values.
 
 .. code-block:: xml
@@ -350,7 +368,7 @@ value-type
 
 The ``value-type`` node indicates that the given C++ type is mapped onto the target
 language as a value type. This means that it is an object passed by value on C++,
-i.e. it is stored in the function call stack. It is a child of the :ref:`typesystem`
+i.e. it is stored in the function call stack. It is a child of the :ref:`typesystem_details`
 node or other type nodes and may contain :ref:`add-function`, :ref:`add-pymethoddef`,
 :ref:`configuration-element`, :ref:`declare-function`, :ref:`conversion-rule`,
 :ref:`enum-type`, :ref:`extra-includes`, :ref:`include-element`, :ref:`modify-function`,
@@ -374,7 +392,8 @@ node or other type nodes and may contain :ref:`add-function`, :ref:`add-pymethod
          stream="yes | no"
          default-constructor="..."
          revision="..."
-         snake-case="yes | no | both" />
+         snake-case="yes | no | both"
+         doc-file = "..." />
     </typesystem>
 
 The **name** attribute is the fully qualified C++ class name, such as
@@ -422,6 +441,9 @@ The *optional* **isNull** and **operator-bool** attributes can be used
 to override the command line setting for generating bool casts
 (see :ref:`bool-cast`).
 
+The *optional* **doc-file** attribute specifies the base name of a ``.qdoc``
+file where documentation of the class is to be found (``qdoc`` only).
+
 .. _object-type:
 
 object-type
@@ -429,7 +451,7 @@ object-type
 
 The object-type node indicates that the given C++ type is mapped onto the target
 language as an object type. This means that it is an object passed by pointer on
-C++ and it is stored on the heap. It is a child of the :ref:`typesystem` node
+C++ and it is stored on the heap. It is a child of the :ref:`typesystem_details` node
 or other type nodes and may contain :ref:`add-function`, :ref:`add-pymethoddef`,
 :ref:`configuration-element`, :ref:`declare-function`, :ref:`enum-type`,
 :ref:`extra-includes`, :ref:`include-element`, :ref:`modify-function`,
@@ -459,7 +481,8 @@ or other type nodes and may contain :ref:`add-function`, :ref:`add-pymethoddef`,
          qt-register-metatype = "yes | no | base"
          stream="yes | no"
          revision="..."
-         snake-case="yes | no | both" />
+         snake-case="yes | no | both"
+         doc-file = "..." />
     </typesystem>
 
 The **name** attribute is the fully qualified C++ class name. If there is no
@@ -539,6 +562,9 @@ attribute.
 For the *optional* **polymorphic-id-expression**, **polymorphic-name-function**
 and **polymorphic-base** attributes, see :ref:`typediscovery-attributes`.
 
+The *optional* **doc-file** attribute specifies the base name of a ``.qdoc``
+file where documentation of the class is to be found (``qdoc`` only).
+
 interface-type
 ^^^^^^^^^^^^^^
 
@@ -551,7 +577,7 @@ container-type
 
 The ``container-type`` node indicates that the given class is a container and
 must be handled using one of the conversion helpers provided by attribute **type**.
-It is a child of the :ref:`typesystem` node and may contain
+It is a child of the :ref:`typesystem_details` node and may contain
 :ref:`conversion-rule` child nodes.
 
 .. code-block:: xml
@@ -596,7 +622,7 @@ opaque-container
 The ``opaque-container`` element can be used to add further instantiations
 of :ref:`opaque containers <opaque-containers>` to existing container types
 (built-in or specified by :ref:`container-type` in included modules).
-It is a child of the :ref:`typesystem` node.
+It is a child of the :ref:`typesystem_details` node.
 
 .. code-block:: xml
 
@@ -616,7 +642,7 @@ The ``typedef-type`` node allows for specifying typedefs in the typesystem. They
 are mostly equivalent to spelling out the typedef in the included header, which
 is often complicated when trying to wrap libraries whose source code cannot be
 easily extended.
-It is a child of the :ref:`typesystem` node or other type nodes.
+It is a child of the :ref:`typesystem_details` node or other type nodes.
 
 .. code-block:: xml
 
@@ -652,7 +678,7 @@ The ``custom-type`` node simply makes the parser aware of the existence of a tar
 language type, thus avoiding errors when trying to find a type used in function
 signatures and other places. The proper handling of the custom type is meant to
 be done by a generator using the APIExractor.
-It is a child of the :ref:`typesystem` node.
+It is a child of the :ref:`typesystem_details` node.
 
 .. code-block:: xml
 
@@ -681,7 +707,7 @@ The ``smart pointer`` type node indicates that the given class is a smart pointe
 and requires inserting calls to **getter** to access the pointeee.
 Currently, the usage is limited to function return values.
 **ref-count-method** specifies the name of the method used to do reference counting.
-It is a child of the :ref:`typesystem` node or other type nodes.
+It is a child of the :ref:`typesystem_details` node or other type nodes.
 
 The *optional* attribute **instantiations** specifies for which instantiations
 of the smart pointer wrappers will be generated (comma-separated list).
@@ -721,6 +747,9 @@ found in the code will be generated. The type name might optionally
 be followed an equal sign and the Python type name, for example
 ``instantiations="int=IntPtr,double=DoublePtr"``.
 It is also possible to specify a namespace delimited by ``::``.
+By default, the type will be in the namespace of the smart pointer,
+for example, ``std`` for ``std::shared_ptr``. Preceding
+the type name by ``::`` causes it to be in the global namespace.
 
 The *optional* attribute **type** specifies the type:
 
@@ -782,7 +811,7 @@ function
 ^^^^^^^^
 
 The ``function`` node indicates that the given C++ global function is mapped
-onto the target language. It is a child of the :ref:`typesystem` node
+onto the target language. It is a child of the :ref:`typesystem_details` node
 and may contain a :ref:`modify-function` child node.
 
 .. code-block:: xml
@@ -822,7 +851,7 @@ file or a system include path (indicated by a trailing slash) to be
 parsed. Normally, include files considered to be system include
 files are skipped by the C++ code parser. Its primary use case
 is exposing classes from the STL library.
-It is a child of the :ref:`typesystem` node.
+It is a child of the :ref:`typesystem_details` node.
 
 .. code-block:: xml
 
index c32eb97d1faf1f9bda08a2ab8e0caefe07842af2..a4ae89a6ff3059dd3adb346042c8aa114f8967df 100644 (file)
@@ -10,7 +10,7 @@ template
 
 The ``template`` node registers a template that can be used to avoid
 duplicate code when extending the generated code, and it is a child of the
-:ref:`typesystem` node.
+:ref:`typesystem_details` node.
 
 .. code-block:: xml
 
index d40891b9b7b01fed6d6b28682fdcd0c58ed9f5f8..5eb5d5abefa2eb3c23b3aeebeb4eea5f528f5a48 100644 (file)
@@ -223,7 +223,7 @@ Variables
 
   .. code-block:: c++
 
-      long a = PyLong_AS_LONG(PyTuple_GET_ITEM(%PYTHON_ARGUMENTS, 0));
+      long a = PyLong_AS_LONG(PyTuple_GetItem(%PYTHON_ARGUMENTS, 0));
 
 
   The generator tries to be smart with attributions, but it will work for the
@@ -241,8 +241,8 @@ Variables
 
   .. code-block:: c++
 
-      Py_DECREF(PyTuple_GET_ITEM(%PYTHON_ARGUMENTS, 0));
-      PyTuple_SET_ITEM(%PYTHON_ARGUMENTS, 0, PyLong_FromLong(10));
+      Py_DECREF(PyTuple_GetItem(%PYTHON_ARGUMENTS, 0));
+      PyTuple_SetItem(%PYTHON_ARGUMENTS, 0, PyLong_FromLong(10));
 
 
 .. _pyself:
index a0132653018ad09dd381680bfbd1011728eb503f..32070fd0af623c97378d8971a10fa0f7d89b25c6 100644 (file)
@@ -32,11 +32,13 @@ using namespace Qt::StringLiterals;
 
 static constexpr auto ENABLE_PYSIDE_EXTENSIONS = "enable-pyside-extensions"_L1;
 static constexpr auto AVOID_PROTECTED_HACK = "avoid-protected-hack"_L1;
+static constexpr auto DISABLED_OPTIMIZATIONS = "unoptimize"_L1;
 
 struct GeneratorOptions
 {
     bool usePySideExtensions = false;
     bool avoidProtectedHack = false;
+    Generator::CodeOptimization optimizations = Generator::AllCodeOptimizations;
 };
 
 struct Generator::GeneratorPrivate
@@ -96,7 +98,9 @@ QList<OptionDescription> Generator::options()
          u"Avoid the use of the '#define protected public' hack."_s},
         {ENABLE_PYSIDE_EXTENSIONS,
          u"Enable PySide extensions, such as support for signal/slots,\n"
-          "use this if you are creating a binding for a Qt-based library."_s}
+          "use this if you are creating a binding for a Qt-based library."_s},
+        {DISABLED_OPTIMIZATIONS,
+         "Disable optimization options"_L1}
     };
 }
 
@@ -106,6 +110,7 @@ public:
     explicit GeneratorOptionsParser(GeneratorOptions *o) : m_options(o) {}
 
     bool handleBoolOption(const QString &key, OptionSource source) override;
+    bool handleOption(const QString &key, const QString &value, OptionSource source) override;
 
 private:
     GeneratorOptions *m_options;
@@ -122,6 +127,32 @@ bool GeneratorOptionsParser::handleBoolOption(const QString & key, OptionSource
     return false;
 }
 
+bool GeneratorOptionsParser::handleOption(const QString & key, const QString & value,
+                                          OptionSource source)
+{
+    if (source == OptionSource::CommandLineSingleDash)
+        return false;
+
+    if (key == DISABLED_OPTIMIZATIONS) {
+        const auto tokens = QStringView{value}.split(u',');
+        for (const auto &tok : tokens) {
+            if (tok == "fullname"_L1)
+                m_options->optimizations.setFlag(Generator::RemoveFullnameField, false);
+            else if (tok == "compression"_L1)
+                m_options->optimizations.setFlag(Generator::CompressSignatureStrings, false);
+            else if (tok == "folding"_L1)
+                m_options->optimizations.setFlag(Generator::FoldCommonTailCode, false);
+            else if (tok == "all"_L1)
+                m_options->optimizations = Generator::CodeOptimization(0);
+            else
+                return false;
+        }
+        return true;
+    }
+
+    return false;
+}
+
 std::shared_ptr<OptionsParser> Generator::createOptionsParser()
 {
     return std::make_shared<GeneratorOptionsParser>(&GeneratorPrivate::m_options);
@@ -203,27 +234,16 @@ void Generator::setOutputDirectory(const QString &outDir)
     m_d->outDir = outDir;
 }
 
-bool Generator::generateFileForContext(const GeneratorContext &context)
+QString Generator::directoryForContext(const GeneratorContext &context) const
 {
-    const auto cls = context.metaClass();
-    auto typeEntry = cls->typeEntry();
-
-    if (!shouldGenerate(typeEntry))
-        return true;
-
-    const QString fileName = fileNameForContext(context);
-    if (fileName.isEmpty())
-        return true;
-
-    QString filePath = outputDirectory() + u'/'
-        + subDirectoryForPackage(typeEntry->targetLangPackage())
-        + u'/' + fileName;
-    FileOut fileOut(filePath);
-
-    generateClass(fileOut.stream, context);
+    return m_d->outDir + u'/'
+        + subDirectoryForPackage(context.metaClass()->typeEntry()->targetLangPackage());
+}
 
-    fileOut.done();
-    return true;
+void Generator::generateSmartPointerClass(TextStream &,
+                                          const QString &,
+                                          const GeneratorContext &)
+{
 }
 
 QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType)
@@ -240,6 +260,7 @@ QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartP
 GeneratorContext Generator::contextForClass(const AbstractMetaClassCPtr &c) const
 {
     GeneratorContext result;
+    result.m_type = GeneratorContext::Class;
     result.m_metaClass = c;
     return result;
 }
@@ -259,22 +280,34 @@ GeneratorContext
 
 bool Generator::generate()
 {
+    QList<GeneratorContext> contexts;
+    contexts.reserve(m_d->api.classes().size());
+
     for (const auto &cls : m_d->api.classes()) {
-        if (!generateFileForContext(contextForClass(cls)))
-            return false;
         auto te = cls->typeEntry();
-        if (shouldGenerate(te) && te->isPrivate())
-            m_d->m_hasPrivateClasses = true;
+        if (shouldGenerate(te)) {
+            contexts.append(contextForClass(cls));
+            if (te->isPrivate())
+                m_d->m_hasPrivateClasses = true;
+        }
+    }
+
+    while (!contexts.isEmpty()) {
+        const auto context = contexts.takeFirst();
+        const QString targetDirectory = directoryForContext(context);
+        FileOut fileOut(targetDirectory + u'/' + fileNameForContext(context));
+        generateClass(fileOut.stream, targetDirectory, context, &contexts);
+        fileOut.done();
     }
 
+    // Generate smart pointers.
     for (const auto &smp: m_d->api.instantiatedSmartPointers()) {
-        AbstractMetaClassCPtr pointeeClass;
-        const auto instantiatedType = smp.type.instantiations().constFirst().typeEntry();
-        if (instantiatedType->isComplex()) // not a C++ primitive
-            pointeeClass = AbstractMetaClass::findClass(m_d->api.classes(), instantiatedType);
-        if (!generateFileForContext(contextForSmartPointer(smp.specialized, smp.type,
-                                                           pointeeClass))) {
-            return false;
+        if (shouldGenerate(smp.specialized->typeEntry())) {
+            const auto context = contextForSmartPointer(smp.specialized, smp.type, smp.pointee);
+            const QString targetDirectory = directoryForContext(context);
+            FileOut fileOut(targetDirectory + u'/' + fileNameForContext(context));
+            generateSmartPointerClass(fileOut.stream, targetDirectory, context);
+            fileOut.done();
         }
     }
     return finishGeneration();
@@ -305,6 +338,11 @@ bool Generator::avoidProtectedHack()
     return GeneratorPrivate::m_options.avoidProtectedHack;
 }
 
+Generator::CodeOptimization Generator::optimizations()
+{
+    return GeneratorPrivate::m_options.optimizations;
+}
+
 QString Generator::getFullTypeName(TypeEntryCPtr type)
 {
     QString result = type->qualifiedCppName();
index 5b051b599dcac74eb7a0909064b900a943e8e927..dbe702478d42b4bb4d89b93356bb6d6e57d756ca 100644 (file)
@@ -30,7 +30,7 @@ QString getFilteredCppSignatureString(QString signature);
  *   you must subclass this to create your own generators.
  */
 class Generator
-{;
+{
 public:
     Q_DISABLE_COPY_MOVE(Generator)
 
@@ -55,6 +55,15 @@ public:
     };
     Q_DECLARE_FLAGS(FileNameFlags, FileNameFlag)
 
+    enum CodeOptimizationFlag {
+        RemoveFullnameField         = 0x00000001,
+        CompressSignatureStrings    = 0x00000002,
+        FoldCommonTailCode          = 0x00000004,
+
+        AllCodeOptimizations        = 0x000000ff
+    };
+    Q_DECLARE_FLAGS(CodeOptimization, CodeOptimizationFlag)
+
     Generator();
     virtual ~Generator();
 
@@ -99,6 +108,8 @@ public:
     /// Returns true if the generated code should not use the
     /// "#define protected public" hack.
     static bool avoidProtectedHack();
+    /// Returns optimization flags.
+    static CodeOptimization optimizations();
 
     /**
      *  Retrieves the name of the currently processed module.
@@ -130,9 +141,6 @@ protected:
         contextForSmartPointer(const AbstractMetaClassCPtr &c, const AbstractMetaType &t,
                            const AbstractMetaClassCPtr &pointeeClass = {});
 
-    /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case).
-    bool generateFileForContext(const GeneratorContext &context);
-
     /// Returns the file base name for a smart pointer.
     static QString getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType);
 
@@ -195,12 +203,18 @@ protected:
     virtual bool doSetup() = 0;
 
     /**
-     *   Write the bindding code for an AbstractMetaClass.
+     *   Write the binding code for an AbstractMetaClass.
      *   This is called by generate method.
      *   \param  s   text stream to write the generated output
      *   \param  metaClass  the class that should be generated
      */
-    virtual void generateClass(TextStream &s, const GeneratorContext &classContext) = 0;
+    virtual void generateClass(TextStream &s,
+                               const QString &targetDir,
+                               const GeneratorContext &classContext,
+                               QList<GeneratorContext> *contexts) = 0;
+    virtual void generateSmartPointerClass(TextStream &s,
+                                           const QString &targetDir,
+                                           const GeneratorContext &classContext);
     virtual bool finishGeneration() = 0;
 
     /**
@@ -220,12 +234,15 @@ protected:
     static QString m_gsp;
 
 private:
+    QString directoryForContext(const GeneratorContext &context) const;
+
     struct GeneratorPrivate;
     GeneratorPrivate *m_d;
 };
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::Options)
 Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::FileNameFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::CodeOptimization)
 
 using GeneratorPtr = std::shared_ptr<Generator>;
 using Generators = QList<GeneratorPtr>;
index b50c2effb8dcf97183a58a87a9c7820ab1429ecc..4021704251e746e2c51c58d83c81cd14e0e11d01 100644 (file)
@@ -16,6 +16,7 @@ QString GeneratorContext::wrapperName() const
 
 QString GeneratorContext::effectiveClassName() const
 {
+    Q_ASSERT(hasClass());
     if (m_type == SmartPointer)
         return m_preciseClassType.cppSignature();
     return m_type == WrappedClass ? m_wrappername : m_metaClass->qualifiedCppName();
index 2e58d43461408663dde079d8efd2d0f3815be232..b604d5f1a2576c0d06b06918d2a4b2102fe0bc54 100644 (file)
@@ -27,16 +27,34 @@ class GeneratorContext {
     friend class ShibokenGenerator;
     friend class Generator;
 public:
-    enum Type { Class, WrappedClass, SmartPointer };
+    enum Type { Class, WrappedClass, SmartPointer,
+                GlobalFunction // No class contained
+              };
 
     GeneratorContext() = default;
 
-    AbstractMetaClassCPtr metaClass() const { return m_metaClass; }
-    const AbstractMetaType &preciseType() const { return m_preciseClassType; }
-    AbstractMetaClassCPtr pointeeClass() const { return m_pointeeClass; }
+    const AbstractMetaClassCPtr &metaClass() const
+    {
+        Q_ASSERT(hasClass());
+        return m_metaClass;
+    }
+
+    const AbstractMetaType &preciseType() const
+    {
+        Q_ASSERT(forSmartPointer());
+        return m_preciseClassType;
+    }
+
+    AbstractMetaClassCPtr pointeeClass() const
+    {
+
+        Q_ASSERT(forSmartPointer());
+        return m_pointeeClass;
+    }
 
     bool forSmartPointer() const { return m_type == SmartPointer; }
     bool useWrapper() const { return m_type ==  WrappedClass; }
+    bool hasClass() const { return m_type != GlobalFunction; }
 
     QString wrapperName() const;
     /// Returns the wrapper name in case of useWrapper(), the qualified class
@@ -48,7 +66,7 @@ private:
     AbstractMetaClassCPtr m_pointeeClass;
     AbstractMetaType m_preciseClassType;
     QString m_wrappername;
-    Type m_type = Class;
+    Type m_type = GlobalFunction;
 };
 
 QDebug operator<<(QDebug debug, const GeneratorContext &c);
index 9871df206cd613705fc83f6ce7070813fde0321f..4fe16648bb837ce2920aad7912726e235de8cd4d 100644 (file)
@@ -234,8 +234,8 @@ void printUsage()
 
 static inline void printVerAndBanner()
 {
-    std::cout << appName << " v" << SHIBOKEN_VERSION << std::endl;
-    std::cout << "Copyright (C) 2016 The Qt Company Ltd." << std::endl;
+    std::cout << appName << " v" << SHIBOKEN_VERSION
+        << "\nCopyright (C) 2016 The Qt Company Ltd.\n";
 }
 
 static inline void errorPrint(const QString &s, const QStringList &arguments)
@@ -348,7 +348,7 @@ int shibokenMain(const QStringList &argV)
     }
 
     if (commonOptions.typeSystemFileName.isEmpty()) {
-        std::cout << "You must specify a Type System file." << std::endl << helpHint;
+        std::cout << "You must specify a Type System file.\n" << helpHint;
         return EXIT_FAILURE;
     }
 
@@ -396,7 +396,7 @@ int shibokenMain(const QStringList &argV)
         TypeDatabase::instance()->logUnmatched();
 
     const QByteArray doneMessage = ReportHandler::doneMessage();
-    std::cout << doneMessage.constData() << std::endl;
+    std::cout << doneMessage.constData() << '\n';
 
     return EXIT_SUCCESS;
 }
@@ -428,7 +428,7 @@ int wmain(int argc, wchar_t *argv[])
     try {
         ex = shibokenMain(argV);
     }  catch (const std::exception &e) {
-        std::cerr << appName << " error: " << e.what() << std::endl;
+        std::cerr << appName << " error: " << e.what() << '\n';
         ex = EXIT_FAILURE;
     }
     return ex;
index 9fb5b9bf61a5f1ab2cd41e27c9f0df9f26a57de7..95912c5ed51a48e059ae28ce57a7f536394573b1 100644 (file)
@@ -28,6 +28,7 @@
 #include <complextypeentry.h>
 #include <flagstypeentry.h>
 #include <primitivetypeentry.h>
+#include <typesystemtypeentry.h>
 #include <qtdocparser.h>
 #include <doxygenparser.h>
 
 #include <QtCore/QJsonArray>
 #include <QtCore/QJsonDocument>
 #include <QtCore/QJsonObject>
+#include <QtCore/QOperatingSystemVersion>
 #include <QtCore/QSet>
 
 #include <algorithm>
 #include <limits>
+#include <unordered_set>
 
 using namespace Qt::StringLiterals;
 
@@ -51,9 +54,21 @@ static inline QString classScope(const AbstractMetaClassCPtr &metaClass)
     return metaClass->fullName();
 }
 
+struct DocClassEntry
+{
+    QString name;
+    QString fullName;
+    QString file;
+};
+
+static bool classEntryLessThan(const DocClassEntry &e1, const DocClassEntry &e2)
+{
+    return e1.name < e2.name;
+}
+
 struct DocPackage
 {
-    QStringList classPages;
+    QList<DocClassEntry> classPages;
     QStringList decoratorPages;
     AbstractMetaFunctionCList globalFunctions;
     AbstractMetaEnumList globalEnums;
@@ -98,6 +113,23 @@ static bool operator<(const GeneratorDocumentation::Property &lhs,
     return lhs.name < rhs.name;
 }
 
+struct ResolvedDocImage
+{
+    QString name;
+    QString absoluteSourceFilePath;
+    QString targetDir;
+};
+
+QDebug operator<<(QDebug debug, const ResolvedDocImage &i)
+{
+    QDebugStateSaver saver(debug);
+    debug.noquote();
+    debug.nospace();
+    debug << "ResolvedDocImage(\"" << i.name << "\", \"" << i.absoluteSourceFilePath
+          << "\" -> \"" << i.targetDir << "\")";
+    return debug;
+}
+
 static QString propertyRefTarget(const QString &name)
 {
     QString result = name;
@@ -201,7 +233,8 @@ static void readExtraDoc(const QFileInfo &fi,
                          DocPackage *docPackage, QStringList *extraTocEntries)
 {
     // Strip to "Property.rst" in output directory
-    const QString newFileName = fi.fileName().mid(moduleName.size() + 1);
+    const QString newFileName = fi.fileName().sliced(moduleName.size() + 1);
+    const QString fullClassName = fi.completeBaseName().sliced(moduleName.size() + 1);
     QFile sourceFile(fi.absoluteFilePath());
     if (!sourceFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
         qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForReading(sourceFile)));
@@ -215,10 +248,15 @@ static void readExtraDoc(const QFileInfo &fi,
         return;
     }
     targetFile.write(contents);
-    if (contents.contains("decorator::"))
+    if (contents.contains("decorator::")) {
         docPackage->decoratorPages.append(newFileName);
-    else
-        docPackage->classPages.append(newFileName);
+    } else {
+        QString name = fullClassName;
+        auto dot = name.lastIndexOf(u'.');
+        if (dot != -1)
+            name.remove(0, dot + 1);
+        docPackage->classPages.append({name, fullClassName, newFileName});
+    }
     extraTocEntries->append(fileNameToTocEntry(newFileName));
 }
 
@@ -325,24 +363,28 @@ QString QtDocGenerator::fileNameForContext(const GeneratorContext &context) cons
 }
 
 void QtDocGenerator::writeFormattedBriefText(TextStream &s, const Documentation &doc,
-                                             const QString &scope) const
+                                             const QString &scope,
+                                             QtXmlToSphinxImages *images) const
 {
-    writeFormattedText(s, doc.brief(), doc.format(), scope);
+    writeFormattedText(s, doc.brief(), doc.format(), scope, images);
 }
 
 void QtDocGenerator::writeFormattedDetailedText(TextStream &s, const Documentation &doc,
-                                                const QString &scope) const
+                                                const QString &scope,
+                                                QtXmlToSphinxImages *images) const
 {
-    writeFormattedText(s, doc.detailed(), doc.format(), scope);
+    writeFormattedText(s, doc.detailed(), doc.format(), scope, images);
 }
 
 void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc,
-                                        Documentation::Format format,
-                                        const QString &scope) const
+                                        DocumentationFormat format,
+                                        const QString &scope,
+                                        QtXmlToSphinxImages *images) const
 {
-    if (format == Documentation::Native) {
+    if (format == DocumentationFormat::Native) {
         QtXmlToSphinx x(this, m_options.parameters, doc, scope);
         s << x;
+        images->append(x.images());
     } else {
         const auto lines = QStringView{doc}.split(u'\n');
         int typesystemIndentation = std::numeric_limits<int>::max();
@@ -404,23 +446,66 @@ static void writeInheritedFromList(TextStream &s, const AbstractMetaClassCPtr &m
         writeInheritanceList(s, res, "Inherits from");
 }
 
-void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+void QtDocGenerator::generateClass(TextStream &s, const QString &targetDir,
+                                   const GeneratorContext &classContext,
+                                   QList<GeneratorContext> *contexts)
 {
-    AbstractMetaClassCPtr metaClass = classContext.metaClass();
-    qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
+    // PYSIDE-2689: Write a (dummy) headline to fix the search contexts.
+    s << headline(classContext.metaClass()->fullName(), '#');
+    generateClassRecursion(s, targetDir, classContext, contexts);
+}
 
-    m_packages[metaClass->package()].classPages << fileNameForContext(classContext);
+static inline TypeSystem::DocMode classDocMode(const AbstractMetaClassCPtr &metaClass)
+{
+    return typeSystemTypeEntry(metaClass->typeEntry())->docMode();
+}
 
-    m_docParser->setPackageName(metaClass->package());
-    m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass));
+void QtDocGenerator::generateClassRecursion(TextStream &s, const QString &targetDir,
+                                            const GeneratorContext &classContext,
+                                            QList<GeneratorContext> *contexts)
+{
+    const AbstractMetaClassCPtr &metaClass = classContext.metaClass();
+
+    qCDebug(lcShibokenDoc, "Generating Documentation for %s", qPrintable(metaClass->fullName()));
+
+    m_packages[metaClass->package()].classPages.append({metaClass->name(), metaClass->fullName(),
+                                                        fileNameForContext(classContext)});
+
+    doGenerateClass(s, targetDir, metaClass);
+
+    if (classDocMode(metaClass) == TypeSystem::DocMode::Nested) {
+        QList<GeneratorContext> innerContexts;
+        for (qsizetype i = 0; i < contexts->size(); ) {
+            if (contexts->at(i).metaClass()->targetLangEnclosingClass() == metaClass)
+                innerContexts.append(contexts->takeAt(i));
+            else
+                ++i;
+        }
+        if (!innerContexts.isEmpty()) {
+            s << indent;
+            for (const auto &innerClassContext : std::as_const(innerContexts))
+                generateClassRecursion(s, targetDir, innerClassContext, contexts);
+            s << outdent;
+        }
+    }
+}
+
+void QtDocGenerator::doGenerateClass(TextStream &s, const QString &targetDir,
+                                     const AbstractMetaClassCPtr &metaClass)
+{
+    qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
+
+    const QString sourceFile =
+        m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass));
 
     s << currentModule(metaClass->package()) << pyClass(metaClass->name());
     Indentation indent(s);
 
+    QtXmlToSphinxImages parsedImages;
     auto documentation = metaClass->documentation();
     const QString scope = classScope(metaClass);
     if (documentation.hasBrief())
-        writeFormattedBriefText(s, documentation, scope);
+        writeFormattedBriefText(s, documentation, scope, &parsedImages);
 
     if (!metaClass->baseClasses().isEmpty()) {
         if (m_options.inheritanceDiagram) {
@@ -460,20 +545,23 @@ void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classC
 
     s << '\n' << headline("Detailed Description") << ".. _More:\n";
 
-    writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass);
-    if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass))
-        writeFormattedDetailedText(s, documentation, scope);
-    writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass);
+    writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass,
+                             &parsedImages);
+    if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, &parsedImages))
+        writeFormattedDetailedText(s, documentation, scope, &parsedImages);
+    writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass, &parsedImages);
 
-    writeEnums(s, metaClass->enums(), scope);
+    writeEnums(s, metaClass->enums(), scope, &parsedImages);
 
     if (!doc.properties.isEmpty())
-        writeProperties(s, doc, metaClass);
+        writeProperties(s, doc, metaClass, &parsedImages);
 
     if (!metaClass->isNamespace())
-        writeFields(s, metaClass);
+        writeFields(s, metaClass, &parsedImages);
 
-    writeFunctions(s, doc.allFunctions, metaClass, scope);
+    writeFunctions(s, doc.allFunctions, metaClass, scope, &parsedImages);
+
+    copyParsedImages(parsedImages, {sourceFile}, targetDir);
 }
 
 void QtDocGenerator::writeFunctionToc(TextStream &s, const QString &title,
@@ -513,7 +601,8 @@ void QtDocGenerator::writePropertyToc(TextStream &s,
 
 void QtDocGenerator::writeProperties(TextStream &s,
                                      const GeneratorDocumentation &doc,
-                                     const AbstractMetaClassCPtr &cppClass) const
+                                     const AbstractMetaClassCPtr &cppClass,
+                                     QtXmlToSphinxImages *images) const
 {
     s << "\n.. note:: Properties can be used directly when "
         << "``from __feature__ import true_property`` is used or via accessor "
@@ -524,8 +613,10 @@ void QtDocGenerator::writeProperties(TextStream &s,
         const QString type = translateToPythonType(prop.type, cppClass, /* createRef */ false);
         s <<  ".. py:property:: " << propertyRefTarget(prop.name)
             << "\n   :type: " << type << "\n\n\n";
-        if (!prop.documentation.isEmpty())
-            writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, scope);
+        if (!prop.documentation.isEmpty()) {
+            writeFormattedText(s, prop.documentation.detailed(), DocumentationFormat::Native,
+                               scope, images);
+        }
         s << "**Access functions:**\n";
         if (prop.getter)
             s << " * " << functionRef(prop.getter->name()) << '\n';
@@ -540,12 +631,12 @@ void QtDocGenerator::writeProperties(TextStream &s,
 }
 
 void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
-                                const QString &scope) const
+                                const QString &scope, QtXmlToSphinxImages *images) const
 {
     for (const AbstractMetaEnum &en : enums) {
         s << pyClass(en.name());
         Indentation indent(s);
-        writeFormattedDetailedText(s, en.documentation(), scope);
+        writeFormattedDetailedText(s, en.documentation(), scope, images);
         const auto version = versionOf(en.typeEntry());
         if (!version.isNull())
             s << rstVersionAdded(version);
@@ -553,14 +644,15 @@ void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums
 
 }
 
-void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const
+void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass,
+                                 QtXmlToSphinxImages *images) const
 {
     constexpr auto section_title = ".. attribute:: "_L1;
 
     const QString scope = classScope(cppClass);
     for (const AbstractMetaField &field : cppClass->fields()) {
         s << section_title << cppClass->fullName() << "." << field.name() << "\n\n";
-        writeFormattedDetailedText(s, field.documentation(), scope);
+        writeFormattedDetailedText(s, field.documentation(), scope, images);
     }
 }
 
@@ -669,26 +761,28 @@ void QtDocGenerator::writeDocSnips(TextStream &s,
     }
 }
 
+void QtDocGenerator::writeFormattedText(TextStream &s, const DocModification &mod,
+                                        const QString &scope, QtXmlToSphinxImages *images) const
+{
+    const bool note = mod.emphasis() == DocumentationEmphasis::LanguageNote;
+    if (note)
+        s << ".. admonition:: Python Language Note\n\n" << indent;
+    writeFormattedText(s, mod.code(), mod.format(), scope, images);
+    if (note)
+        s << outdent;
+}
+
 bool QtDocGenerator::writeDocModifications(TextStream &s,
                                            const DocModificationList &mods,
                                            TypeSystem::DocModificationMode mode,
-                                           const QString &scope) const
+                                           const QString &scope,
+                                           QtXmlToSphinxImages *images) const
 {
     bool didSomething = false;
     for (const DocModification &mod : mods) {
         if (mod.mode() == mode) {
-            switch (mod.format()) {
-            case TypeSystem::NativeCode:
-                writeFormattedText(s, mod.code(), Documentation::Native, scope);
-                didSomething = true;
-                break;
-            case TypeSystem::TargetLangCode:
-                writeFormattedText(s, mod.code(), Documentation::Target, scope);
-                didSomething = true;
-                break;
-            default:
-                break;
-            }
+            writeFormattedText(s, mod, scope, images);
+            didSomething = true;
         }
     }
     return didSomething;
@@ -696,11 +790,12 @@ bool QtDocGenerator::writeDocModifications(TextStream &s,
 
 bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
                                               TypeSystem::DocModificationMode mode,
-                                              const AbstractMetaClassCPtr &cppClass) const
+                                              const AbstractMetaClassCPtr &cppClass,
+                                              QtXmlToSphinxImages *images) const
 {
     const bool didSomething =
         writeDocModifications(s, DocParser::getDocModifications(cppClass),
-                              mode, classScope(cppClass));
+                              mode, classScope(cppClass), images);
     s << '\n';
 
     // FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
@@ -715,9 +810,10 @@ bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
                                               TypeSystem::DocModificationMode mode,
                                               const DocModificationList &modifications,
                                               const AbstractMetaFunctionCPtr &func,
-                                              const QString &scope) const
+                                              const QString &scope,
+                                              QtXmlToSphinxImages *images) const
 {
-    const bool didSomething = writeDocModifications(s, modifications, mode, scope);
+    const bool didSomething = writeDocModifications(s, modifications, mode, scope, images);
     s << '\n';
 
     // FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
@@ -778,7 +874,8 @@ QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
         if (createRef)
             enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
         return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName);
-    } else if (type.isEnum()) {
+    }
+    if (type.isEnum()) {
         auto enumTypeEntry = std::static_pointer_cast<const EnumTypeEntry>(type.typeEntry());
         auto enumName = enumTypeEntry->targetLangName();
         if (createRef)
@@ -868,19 +965,21 @@ static bool containsFunctionDirective(const DocModification &dm)
 }
 
 void QtDocGenerator::writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
-                                    const AbstractMetaClassCPtr &cppClass, const QString &scope)
+                                    const AbstractMetaClassCPtr &cppClass, const QString &scope,
+                                    QtXmlToSphinxImages *images) const
 {
     QString lastName;
     for (const auto &func : funcs) {
         const bool indexed = func->name() != lastName;
         lastName = func->name();
-        writeFunction(s, func, cppClass, scope, indexed);
+        writeFunction(s, func, images, cppClass, scope, indexed);
     }
 }
 
 void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+                                   QtXmlToSphinxImages *images,
                                    const AbstractMetaClassCPtr &cppClass,
-                                   const QString &scope, bool indexed)
+                                   const QString &scope, bool indexed) const
 {
     const auto modifications = DocParser::getDocModifications(func, cppClass);
 
@@ -907,7 +1006,7 @@ void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr
             s << rstDeprecationNote("function");
     }
 
-    writeFunctionDocumentation(s, func, modifications, scope);
+    writeFunctionDocumentation(s, func, modifications, scope, images);
 
     if (auto propIndex = func->propertySpecIndex(); propIndex >= 0) {
         const QString name = cppClass->propertySpecs().at(propIndex).name();
@@ -925,45 +1024,22 @@ void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr
 
 void QtDocGenerator::writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
                                                 const DocModificationList &modifications,
-                                                const QString &scope) const
+                                                const QString &scope,
+                                                QtXmlToSphinxImages *images) const
 
 {
-    writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications, func, scope);
-    if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications, func, scope)) {
-        writeFormattedBriefText(s, func->documentation(), scope);
-        writeFormattedDetailedText(s, func->documentation(), scope);
+    writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications,
+                             func, scope, images);
+    if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications,
+                                  func, scope, images)) {
+        writeFormattedBriefText(s, func->documentation(), scope, images);
+        writeFormattedDetailedText(s, func->documentation(), scope, images);
     }
-    writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications, func, scope);
+    writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications,
+                             func, scope, images);
 }
 
-static QStringList fileListToToc(const QStringList &items)
-{
-    QStringList result;
-    result.reserve(items.size());
-    std::transform(items.cbegin(), items.cend(), std::back_inserter(result),
-                   fileNameToTocEntry);
-    return result;
-}
-
-static QStringList functionListToToc(const AbstractMetaFunctionCList &functions)
-{
-    QStringList result;
-    result.reserve(functions.size());
-    for (const auto &f : functions)
-        result.append(f->name());
-    // Functions are sorted by the Metabuilder; erase overloads
-    result.erase(std::unique(result.begin(), result.end()), result.end());
-    return result;
-}
-
-static QStringList enumListToToc(const AbstractMetaEnumList &enums)
-{
-    QStringList result;
-    result.reserve(enums.size());
-    for (const auto &e : enums)
-        result.append(e.name());
-    return result;
-}
+using TocMap = QMap<QChar, QStringList>;
 
 // Sort entries for a TOC by first character, dropping the
 // leading common Qt prefixes like 'Q'.
@@ -984,20 +1060,63 @@ static QChar sortKey(const QString &key)
     return idx < size ? key.at(idx).toUpper() : u'A';
 }
 
+static TocMap classEntryListToToc(const QList<DocClassEntry> &entries,
+                                  TypeSystem::DocMode docMode)
+{
+    const bool fullyQualified = docMode == TypeSystem::DocMode::Nested;
+    TocMap result;
+    // Sort by name, use full href
+    for (const auto &e : entries)
+         result[sortKey(e.name)] << (fullyQualified ? e.fullName : e.name);
+    return result;
+}
+
+static TocMap fileListToToc(const QStringList &items)
+{
+    TocMap result;
+    for (const auto &item : items) {
+        const QString entry = fileNameToTocEntry(item);
+        result[sortKey(entry)] << entry;
+    }
+    return result;
+}
+
+static TocMap functionListToToc(const AbstractMetaFunctionCList &functions)
+{
+    TocMap result;
+    // Functions are sorted by the Metabuilder; erase overloads
+    std::unordered_set<QString> seenNames;
+    for (const auto &f : functions) {
+        const QString &name = f->name();
+        if (seenNames.find(name) == seenNames.end()) {
+            seenNames.insert(name);
+            result[sortKey(name)] << name;
+        }
+    }
+    return result;
+}
+
+static TocMap enumListToToc(const AbstractMetaEnumList &enums)
+{
+    TocMap result;
+    for (const auto &e : enums)
+        result[sortKey(e.name())] << e.name();
+    return result;
+}
+
 static void writeFancyToc(TextStream& s, QAnyStringView title,
-                          const QStringList& items,
+                          const TocMap &tocMap,
                           QLatin1StringView referenceType)
 {
-    using TocMap = QMap<QChar, QStringList>;
-
-    if (items.isEmpty())
+    if (tocMap.isEmpty())
         return;
 
-    TocMap tocMap;
-    for (const QString &item : items)
-        tocMap[sortKey(item)] << item;
-
-    static const qsizetype numColumns = 4;
+    qsizetype maxColumnCount = 0;
+    for (auto it = tocMap.cbegin(), end = tocMap.cend(); it != end; ++it) {
+        if (it.value().size() > maxColumnCount)
+            maxColumnCount = it.value().size();
+    }
+    const auto columnCount = 1 + std::min(maxColumnCount, qsizetype(3)); // With header
 
     QtXmlToSphinx::Table table;
     for (auto it = tocMap.cbegin(), end = tocMap.cend(); it != end; ++it) {
@@ -1005,16 +1124,25 @@ static void writeFancyToc(TextStream& s, QAnyStringView title,
         const QString charEntry = u"**"_s + it.key() + u"**"_s;
         row << QtXmlToSphinx::TableCell(charEntry);
         for (const QString &item : std::as_const(it.value())) {
-            if (row.size() >= numColumns) {
+            if (row.size() >= columnCount) {
                 table.appendRow(row);
                 row.clear();
                 row << QtXmlToSphinx::TableCell(QString{});
             }
-            const QString entry = "* :"_L1 + referenceType + ":`"_L1 + item + u'`';
+            QString entry = "* :"_L1 + referenceType + ":`"_L1;
+            if (item.contains(u'.'))
+                entry += u'~';
+            entry += item + u'`';
             row << QtXmlToSphinx::TableCell(entry);
         }
-        if (row.size() > 1)
+        if (row.size() > 1) {
+            if (const auto padColSpan = columnCount - row.size(); padColSpan > 0) {
+                QtXmlToSphinx::TableCell padding(QString{});
+                padding.colSpan = int(padColSpan);
+                row.append(padding);
+            }
             table.appendRow(row);
+        }
     }
 
     table.normalize();
@@ -1048,7 +1176,7 @@ bool QtDocGenerator::writeInheritanceFile()
 {
     QFile inheritanceFile(m_options.inheritanceFile);
     if (!inheritanceFile.open(QIODevice::WriteOnly | QIODevice::Text))
-        throw Exception(msgCannotOpenForWriting(m_options.inheritanceFile));
+        throw Exception(msgCannotOpenForWriting(inheritanceFile));
 
     QJsonObject dict;
     for (const auto &c : api().classes()) {
@@ -1077,16 +1205,41 @@ static inline void removeExtraDocs(const QStringList &extraTocEntries,
                      functions->end());
 }
 
+// Extract images from RstDocs
+static bool imagesFromRstDocs(const QByteArray &rstDoc, const QString &scope,
+                              QtXmlToSphinxImages *images)
+{
+    bool result = false;
+    static const QByteArray imageTag = ".. image:: "_ba;
+
+    for (qsizetype pos = 0; pos < rstDoc.size(); ) {
+        pos = rstDoc.indexOf(imageTag, pos);
+        if (pos == -1)
+            break;
+        pos += imageTag.size();
+        const auto newLinePos = rstDoc.indexOf('\n', pos);
+        if (newLinePos == -1)
+            break;
+        const auto image = rstDoc.sliced(pos, newLinePos - pos).trimmed();
+        images->append({scope, QString::fromUtf8(image)});
+        result = true;
+        pos = newLinePos + 1;
+    }
+    return result;
+}
+
 void QtDocGenerator::writeModuleDocumentation()
 {
+    auto *typeDb = TypeDatabase::instance();
     for (auto it = m_packages.begin(), end = m_packages.end(); it != end; ++it) {
         auto &docPackage = it.value();
-        std::sort(docPackage.classPages.begin(), docPackage.classPages.end());
+        std::sort(docPackage.classPages.begin(), docPackage.classPages.end(), classEntryLessThan);
 
         QString key = it.key();
         key.replace(u'.', u'/');
         QString outputDir = outputDirectory() + u'/' + key;
         FileOut output(outputDir + u"/index.rst"_s);
+        QtXmlToSphinxImages parsedImages;
         TextStream& s = output.stream;
 
         const QString &title = it.key();
@@ -1096,7 +1249,7 @@ void QtDocGenerator::writeModuleDocumentation()
         // information when neeeded. For example, the RST files in the extras directory
         // doesn't include the PySide# prefix in their names.
         QString moduleName = it.key();
-        const int lastIndex = moduleName.lastIndexOf(u'.');
+        const auto lastIndex = moduleName.lastIndexOf(u'.');
         if (lastIndex >= 0)
             moduleName.remove(0, lastIndex + 1);
 
@@ -1128,30 +1281,53 @@ void QtDocGenerator::writeModuleDocumentation()
             << ":maxdepth: 1\n\n";
         if (hasGlobals)
             s << globalsPage << '\n';
-        for (const QString &className : std::as_const(docPackage.classPages))
-            s << className << '\n';
+        for (const auto &e : std::as_const(docPackage.classPages))
+            s << e.file << '\n';
         s << "\n\n" << outdent << outdent << headline("Detailed Description");
 
         // module doc is always wrong and C++istic, so go straight to the extra directory!
-        QFile moduleDoc(m_options.extraSectionDir + u'/' + moduleName
-                        + u".rst"_s);
-        if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) {
-            s << moduleDoc.readAll();
+        const QString moduleDocRstFileName = m_options.extraSectionDir + u'/' + moduleName + u".rst"_s;
+        QStringList sourceFileNames;
+
+        QString context = it.key();
+        // Get the WebXML source file for image resolution if we
+        // are re-using images from it in our .rst.
+        QtXmlToSphinx::stripPythonQualifiers(&context);
+        const ModuleDocumentation moduleDocumentation =
+            m_docParser->retrieveModuleDocumentation(it.key());
+        const Documentation &webXmlModuleDoc = moduleDocumentation.documentation;
+        if (webXmlModuleDoc.hasSourceFile())
+            sourceFileNames.append(webXmlModuleDoc.sourceFile());
+        if (QFileInfo::exists(moduleDocRstFileName)) {
+            QFile moduleDoc(moduleDocRstFileName);
+            if (!moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text))
+                throw Exception(msgCannotOpenForReading(moduleDoc));
+            const QByteArray rstDoc = moduleDoc.readAll();
+            s << rstDoc;
             moduleDoc.close();
-        } else {
+            if (imagesFromRstDocs(rstDoc, context, &parsedImages))
+                sourceFileNames.append(moduleDocRstFileName);
+        } else if (!webXmlModuleDoc.isEmpty()) {
             // try the normal way
-            Documentation moduleDoc = m_docParser->retrieveModuleDocumentation(it.key());
-            if (moduleDoc.format() == Documentation::Native) {
-                QString context = it.key();
-                QtXmlToSphinx::stripPythonQualifiers(&context);
-                QtXmlToSphinx x(this, m_options.parameters, moduleDoc.detailed(), context);
+            if (webXmlModuleDoc.format() == DocumentationFormat::Native) {
+                QtXmlToSphinx x(this, m_options.parameters, webXmlModuleDoc.detailed(), context);
                 s << x;
+                parsedImages += x.images();
             } else {
-                s << moduleDoc.detailed();
+                s << webXmlModuleDoc.detailed();
             }
         }
 
-        writeFancyToc(s, "List of Classes", fileListToToc(docPackage.classPages),
+        if (!moduleDocumentation.qmlTypesUrl.isEmpty()) {
+            s << '\n' << headline("List of QML types")
+              << "\n    * `" << moduleName<< " QML Types <"
+              << moduleDocumentation.qmlTypesUrl << ">`_\n\n";
+        }
+
+        TypeSystemTypeEntryCPtr typeSystemEntry = typeDb->findTypeSystemType(it.key());
+        Q_ASSERT(typeSystemEntry);
+        writeFancyToc(s, "List of Classes", classEntryListToToc(docPackage.classPages,
+                                                                typeSystemEntry->docMode()),
                       "class"_L1);
         writeFancyToc(s, "List of Decorators", fileListToToc(docPackage.decoratorPages),
                       "deco"_L1);
@@ -1161,6 +1337,7 @@ void QtDocGenerator::writeModuleDocumentation()
                       "any"_L1);
 
         output.done();
+        copyParsedImages(parsedImages, sourceFileNames, outputDir);
 
         if (hasGlobals)
             writeGlobals(it.key(), outputDir + u'/' + globalsPage, docPackage);
@@ -1172,20 +1349,33 @@ void QtDocGenerator::writeGlobals(const QString &package,
                                   const DocPackage &docPackage)
 {
     FileOut output(fileName);
+    QStringList docFiles;
+    QtXmlToSphinxImages parsedImages;
     TextStream &s = output.stream;
 
     // Write out functions with injected documentation
     if (!docPackage.globalFunctions.isEmpty()) {
+        for (const auto &func : docPackage.globalFunctions) {
+            const Documentation &doc = func->documentation();
+            if (doc.hasSourceFile() && !docFiles.contains(doc.sourceFile()))
+                docFiles.append(doc.sourceFile());
+        }
         s << currentModule(package) << headline("Functions");
-        writeFunctions(s, docPackage.globalFunctions, {}, {});
+        writeFunctions(s, docPackage.globalFunctions, {}, {}, &parsedImages);
     }
 
     if (!docPackage.globalEnums.isEmpty()) {
         s << headline("Enumerations");
-        writeEnums(s, docPackage.globalEnums, package);
+        for (const auto &globalEnum : docPackage.globalEnums) {
+            const Documentation &doc = globalEnum.documentation();
+            if (doc.hasSourceFile() && !docFiles.contains(doc.sourceFile()))
+                docFiles.append(doc.sourceFile());
+        }
+        writeEnums(s, docPackage.globalEnums, package, &parsedImages);
     }
 
     output.done();
+    copyParsedImages(parsedImages, docFiles, QFileInfo(fileName).absolutePath());
 }
 
 static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
@@ -1198,6 +1388,27 @@ static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
     return result;
 }
 
+// Return rst target document name for additional docs
+// "qtcore/webxml/animation.webxml" -> "qtcore-animation.rst"
+static QString additionalDocRstFileName(const QFileInfo &fi, const QString &rstSuffix)
+{
+    QString result = fi.baseName() + rstSuffix;
+    // Disambiguate file name by directory
+    const QString dirName = fi.absolutePath();
+    QStringView prefix{dirName};
+    if (prefix.endsWith("/webxml"_L1))
+        prefix = prefix.chopped(7);
+    auto lastSlash = prefix.lastIndexOf(u'/');
+    if (lastSlash != -1) {
+        prefix = prefix.sliced(lastSlash + 1);
+        if (!result.startsWith(prefix)) {
+            result.prepend(u'-');
+            result.prepend(prefix);
+        }
+    }
+    return result;
+}
+
 void QtDocGenerator::writeAdditionalDocumentation() const
 {
     QFile additionalDocumentationFile(m_options.additionalDocumentationList);
@@ -1236,7 +1447,7 @@ void QtDocGenerator::writeAdditionalDocumentation() const
             // Normal file entry
             QFileInfo fi(m_options.parameters.docDataDir + u'/' + line);
             if (fi.isFile()) {
-                const QString rstFileName = fi.baseName() + rstSuffix;
+                const QString rstFileName = additionalDocRstFileName(fi, rstSuffix);
                 const QString rstFile = targetDir + u'/' + rstFileName;
                 const QString context = targetDir.mid(targetDir.lastIndexOf(u'/') + 1);
                 if (convertToRst(fi.absoluteFilePath(),
@@ -1263,17 +1474,11 @@ void QtDocGenerator::writeAdditionalDocumentation() const
            successCount, count);
 }
 
-#ifdef __WIN32__
-#   define PATH_SEP ';'
-#else
-#   define PATH_SEP ':'
-#endif
-
 bool QtDocGenerator::doSetup()
 {
     if (m_options.parameters.codeSnippetDirs.isEmpty()) {
         m_options.parameters.codeSnippetDirs =
-            m_options.parameters.libSourceDir.split(QLatin1Char(PATH_SEP));
+            m_options.parameters.libSourceDir.split(QDir::listSeparator());
     }
 
     if (m_docParser.isNull()) {
@@ -1291,7 +1496,6 @@ bool QtDocGenerator::doSetup()
     }
 
     m_docParser->setDocumentationDataDirectory(m_options.parameters.docDataDir);
-    m_docParser->setLibrarySourceDirectory(m_options.parameters.libSourceDir);
     m_options.parameters.outputDirectory = outputDirectory();
     return true;
 }
@@ -1357,7 +1561,7 @@ bool QtDocGeneratorOptionsParser::handleOption(const QString &key, const QString
         return true;
     }
     if (key == u"documentation-code-snippets-dir") {
-        m_options->parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP));
+        m_options->parameters.codeSnippetDirs = value.split(QDir::listSeparator());
         return true;
     }
 
@@ -1409,12 +1613,12 @@ bool QtDocGenerator::convertToRst(const QString &sourceFileName,
             *errorMessage = msgCannotOpenForReading(sourceFile);
         return false;
     }
-    const QString doc = QString::fromUtf8(sourceFile.readAll());
-    sourceFile.close();
 
     FileOut targetFile(targetFileName);
-    QtXmlToSphinx x(this, m_options.parameters, doc, context);
+    QtXmlToSphinx x(this, m_options.parameters, sourceFile, context);
     targetFile.stream << x;
+    copyParsedImages(x.images(), {sourceFileName},
+                     QFileInfo(targetFileName).absolutePath());
     targetFile.done();
     return true;
 }
@@ -1566,3 +1770,121 @@ QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) con
     }
     return resolved;
 }
+
+// Determine image target directory from context,
+// "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui" for the rare case of resolving
+// an image by scope/module.
+// FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or
+// nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint".
+static QString imageRelativeTargetDirFromContext(const QString &scope,
+                                                 const QString &hrefBase)
+{
+    QString relativeTargetDir = scope;
+    const auto lastDot = relativeTargetDir.lastIndexOf(u'.');
+    if (lastDot != -1)
+        relativeTargetDir.truncate(lastDot);
+    relativeTargetDir.replace(u'.', u'/');
+    relativeTargetDir += hrefBase;
+    return relativeTargetDir;
+}
+
+ResolvedDocImage
+QtDocGenerator::resolveImage(const QtXmlToSphinxImage &image,
+                             const QStringList &sourceDirs,
+                             const QString &targetDir) const
+{
+    QString hrefBase;
+    QString hrefName = image.href; // split "images/a.png"
+    if (const auto lastSlash = image.href.lastIndexOf(u'/'); lastSlash != -1) {
+        hrefName = image.href.sliced(lastSlash + 1);
+        hrefBase = u'/' + image.href.sliced(0, lastSlash);
+    }
+
+    QStringList candidates;
+    // 1st: Resolve relative to document
+    for (const auto &sourceDir : sourceDirs) {
+        candidates.append(sourceDir + u'/' + image.href);
+        if (QFileInfo::exists(candidates.constLast()))
+            return {hrefName, candidates.constLast(), targetDir + hrefBase};
+    }
+
+    // 2nd: Check in docDataDir (doxygen) FIXME PYSIDE 7: Is this still needed?
+    candidates.append(m_options.parameters.docDataDir + u'/' + image.href);
+    if (QFileInfo::exists(candidates.constLast())) {
+        return {hrefName, candidates.constLast(),
+                m_options.parameters.outputDirectory + hrefBase};
+    }
+
+    // 3rd: Try to resolve via scope/module (relative to output directory).
+    // It should rarely happen since resolution against the source file should
+    // mostly work, only for some cases of injected XML documentation.
+    if (!m_options.doxygen && !image.scope.isEmpty()) {
+        candidates.append(m_options.parameters.docDataDir + u'/' +
+                          QtDocParser::qdocModuleDir(image.scope) + u'/' + image.href);
+        if (QFileInfo::exists(candidates.constLast())) {
+            return {hrefName, candidates.constLast(),
+                    m_options.parameters.outputDirectory + u'/'
+                    + imageRelativeTargetDirFromContext(image.scope, hrefBase)};
+        }
+    }
+
+    throw Exception(msgCannotFindImage(image.href, image.scope, candidates));
+    return {};
+}
+
+static constexpr Qt::CaseSensitivity fileSysCaseSensitivity =
+    QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows
+        ? Qt::CaseInsensitive : Qt::CaseSensitive;
+
+// Poor man's relative-to-dir function.
+static QString relativeTo(const QString &path, const QString &dir)
+{
+    const auto dirSize = dir.size();
+    const auto pathSize = path.size();
+    if (dirSize == pathSize && path.compare(dir, fileSysCaseSensitivity) == 0)
+        return {};
+    if (path.size() > dirSize && path.startsWith(dir, fileSysCaseSensitivity)
+        && path.at(dirSize) == u'/') {
+        return path.sliced(dirSize + 1);
+    }
+
+    throw Exception(msgNotRelative(path, dir));
+    return {};
+}
+
+// Copy a parsed image from WebXML to doc/base, creating all needed directories
+static void copyParsedImage(const ResolvedDocImage &image, QDir &targetDir)
+{
+    const QString targetFileName = image.targetDir + u'/' + image.name;
+    if (QFileInfo::exists(targetFileName))
+        return;
+
+    if (!QFileInfo::exists(image.targetDir)
+        && !targetDir.mkpath(relativeTo(image.targetDir, targetDir.absolutePath()))) {
+        throw Exception(msgCannotCreateDir(image.targetDir));
+    }
+    QFile source(image.absoluteSourceFilePath);
+    if (!source.copy(targetFileName))
+        throw Exception(msgCannotCopy(source, targetFileName));
+}
+
+// Copy parsed images from WebXML to doc/base
+void QtDocGenerator::copyParsedImages(const QtXmlToSphinxImages &images,
+                                      const QStringList &sourceDocumentFiles,
+                                      const QString &targetDocumentDir) const
+{
+    if (images.isEmpty())
+        return;
+
+    QStringList sourceDirs;
+    for (const auto &sourceDocumentFile : sourceDocumentFiles) {
+        const QString sourceDir = QFileInfo(sourceDocumentFile).absolutePath();
+        if (!sourceDirs.contains(sourceDir))
+            sourceDirs.append(sourceDir);
+    }
+
+    QDir targetDir(targetDocumentDir);
+
+    for (const auto &image : images)
+         copyParsedImage(resolveImage(image, sourceDirs, targetDocumentDir), targetDir);
+}
index 3b1c82e74485d877809d6e2c77ed48fcc81925e1..ce6a5f4a09df23147a0e57654abad0237883c495 100644 (file)
@@ -19,6 +19,8 @@ struct DocGeneratorOptions;
 struct GeneratorDocumentation;
 struct DocPackage;
 
+struct ResolvedDocImage;
+
 /**
 *   The DocGenerator generates documentation from library being binded.
 */
@@ -28,7 +30,7 @@ public:
     Q_DISABLE_COPY_MOVE(QtDocGenerator)
 
     QtDocGenerator();
-    ~QtDocGenerator();
+    ~QtDocGenerator() override;
 
     bool doSetup() override;
 
@@ -56,22 +58,33 @@ protected:
     bool shouldGenerate(const TypeEntryCPtr &) const override;
     static QString fileNameSuffix();
     QString fileNameForContext(const GeneratorContext &context) const override;
-    void generateClass(TextStream &ts, const GeneratorContext &classContext) override;
+    void generateClass(TextStream &ts, const QString &targetDir,
+                       const GeneratorContext &classContext,
+                       QList<GeneratorContext> *contexts) override;
     bool finishGeneration() override;
 
 private:
+    void generateClassRecursion(TextStream &s, const QString &targetDir,
+                                const GeneratorContext &classContext,
+                                QList<GeneratorContext> *contexts);
+    void doGenerateClass(TextStream &ts, const QString &targetDir,
+                         const AbstractMetaClassCPtr &metaClass);
     void writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
-                    const QString &scope) const;
+                    const QString &scope, QtXmlToSphinxImages *images) const;
 
-    void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const;
+    void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass,
+                     QtXmlToSphinxImages *images) const;
     void writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
-                        const AbstractMetaClassCPtr &cppClass, const QString &scope);
+                        const AbstractMetaClassCPtr &cppClass, const QString &scope,
+                        QtXmlToSphinxImages *images) const;
     void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+                       QtXmlToSphinxImages *images,
                        const AbstractMetaClassCPtr &cppClass = {},
-                       const QString &scope = {}, bool indexed = true);
+                       const QString &scope = {}, bool indexed = true) const;
     void writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
                                     const DocModificationList &modifications,
-                                    const QString &scope) const;
+                                    const QString &scope,
+                                    QtXmlToSphinxImages *images) const;
     void writeFunctionParametersType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
                                      const AbstractMetaFunctionCPtr &func) const;
     static void writeFunctionToc(TextStream &s, const QString &title,
@@ -80,26 +93,34 @@ private:
                                  const GeneratorDocumentation &doc);
     void writeProperties(TextStream &s,
                          const GeneratorDocumentation &doc,
-                         const AbstractMetaClassCPtr &cppClass) const;
+                         const AbstractMetaClassCPtr &cppClass,
+                         QtXmlToSphinxImages *images) const;
     void writeParameterType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
                             const AbstractMetaArgument &arg) const;
+    void writeFormattedText(TextStream &s, const DocModification &mod,
+                            const QString &scope, QtXmlToSphinxImages *images) const;
     void writeFormattedText(TextStream &s, const QString &doc,
-                            Documentation::Format format,
-                            const QString &scope = {}) const;
+                            DocumentationFormat format,
+                            const QString &scope,
+                            QtXmlToSphinxImages *images) const;
     void writeFormattedBriefText(TextStream &s, const Documentation &doc,
-                                 const QString &scope = {}) const;
+                                 const QString &scope, QtXmlToSphinxImages *images) const;
     void writeFormattedDetailedText(TextStream &s, const Documentation &doc,
-                                    const QString &scope = {}) const;
+                                    const QString &scope,
+                                    QtXmlToSphinxImages *images) const;
 
     bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
-                                  const AbstractMetaClassCPtr &cppClass) const;
+                                  const AbstractMetaClassCPtr &cppClass,
+                                  QtXmlToSphinxImages *images) const;
     bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
                                   const DocModificationList &modifications,
                                   const AbstractMetaFunctionCPtr &func,
-                                  const QString &scope = {}) const;
+                                  const QString &scope,
+                                  QtXmlToSphinxImages *images) const;
     bool writeDocModifications(TextStream &s, const DocModificationList &mods,
                                TypeSystem::DocModificationMode mode,
-                               const QString &scope = {}) const;
+                               const QString &scope,
+                               QtXmlToSphinxImages *images) const;
     static void writeDocSnips(TextStream &s, const CodeSnipList &codeSnips,
                               TypeSystem::CodeSnipPosition position, TypeSystem::Language language);
 
@@ -108,7 +129,12 @@ private:
                       const DocPackage &docPackage);
     void writeAdditionalDocumentation() const;
     bool writeInheritanceFile();
-
+    ResolvedDocImage resolveImage(const QtXmlToSphinxImage &image,
+                                  const QStringList &sourceDirs,
+                                  const QString &targetDir) const;
+    void copyParsedImages(const QtXmlToSphinxImages &images,
+                          const QStringList &sourceDocumentFiles,
+                          const QString &targetDocumentFile) const;
     QString translateToPythonType(const AbstractMetaType &type,
                                   const AbstractMetaClassCPtr &cppClass,
                                   bool createRef = true) const;
index 55c1d2090f1beb517ec63c8b815c6775211d126d..a60a288daef70e354fb69fcf7bfeb0da6f126e27 100644 (file)
 
 using namespace Qt::StringLiterals;
 
+QDebug operator<<(QDebug debug, const QtXmlToSphinxImage &i)
+{
+    QDebugStateSaver saver(debug);
+    debug.noquote();
+    debug.nospace();
+    debug << "QtXmlToSphinxImage(\"" << i.href << "\", \"" << i.scope << "\")";
+    return debug;
+}
+
+// Helpers for extracting qdoc snippets "#/// [id]"
+static QString fileNameOfDevice(const QIODevice *inputFile, QAnyStringView defaultName = "<stdin>")
+{
+    if (inputFile != nullptr) {
+        if (const auto *file = qobject_cast<const QFile *>(inputFile))
+            return QDir::toNativeSeparators(file->fileName());
+    }
+    return defaultName.toString();
+}
+
 QString msgTagWarning(const QXmlStreamReader &reader, const QString &context,
                       const QString &tag, const QString &message)
 {
@@ -265,14 +284,31 @@ static const WebXmlTagHash &webXmlTagHash()
 
 QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
                              const QtXmlToSphinxParameters &parameters,
-                             const QString& doc, const QString& context)
-        : m_output(static_cast<QString *>(nullptr)),
-          m_context(context),
-          m_generator(docGenerator), m_parameters(parameters)
+                             const QString& doc, const QString& context) :
+    QtXmlToSphinx(docGenerator, parameters, context)
 {
     m_result = transform(doc);
 }
 
+QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+                             const QtXmlToSphinxParameters &parameters,
+                             QIODevice &ioDevice,
+                             const QString& context) :
+    QtXmlToSphinx(docGenerator, parameters, context)
+{
+    QXmlStreamReader reader(&ioDevice);
+    m_result = transform(reader);
+}
+
+QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+                             const QtXmlToSphinxParameters &parameters,
+                             const QString& context)
+    : m_output(static_cast<QString *>(nullptr)),
+    m_context(context),
+    m_generator(docGenerator), m_parameters(parameters)
+{
+}
+
 QtXmlToSphinx::~QtXmlToSphinx() = default;
 
 void QtXmlToSphinx::callHandler(WebXmlTag t, QXmlStreamReader &r)
@@ -463,9 +499,13 @@ QString QtXmlToSphinx::transform(const QString& doc)
     if (doc.trimmed().isEmpty())
         return doc;
 
-    pushOutputBuffer();
-
     QXmlStreamReader reader(doc);
+    return transform(reader);
+}
+
+QString QtXmlToSphinx::transform(QXmlStreamReader& reader)
+{
+    pushOutputBuffer();
 
     m_output << autoTranslatedPlaceholder;
     Indentation indentation(m_output);
@@ -474,10 +514,9 @@ QString QtXmlToSphinx::transform(const QString& doc)
         QXmlStreamReader::TokenType token = reader.readNext();
         if (reader.hasError()) {
             QString message;
-            QTextStream(&message) << "XML Error "
-                << reader.errorString() << " at " << reader.lineNumber()
-                << ':' << reader.columnNumber() << '\n' << doc;
-            m_output << message;
+            QTextStream(&message) << fileNameOfDevice(reader.device())
+                << ':' << reader.lineNumber() << ':' << reader.columnNumber()
+                << ": XML Error: " << reader.errorString();
             throw Exception(message);
             break;
         }
@@ -512,6 +551,7 @@ QString QtXmlToSphinx::transform(const QString& doc)
     Q_ASSERT(m_buffers.isEmpty());
     setAutoTranslatedNote(&retval);
     return retval;
+
 }
 
 static QString resolveFile(const QStringList &locations, const QString &path)
@@ -609,13 +649,6 @@ QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &pa
     return {{}, Snippet::Error};
 }
 
-// Helpers for extracting qdoc snippets "#/// [id]"
-static QString fileNameOfDevice(const QIODevice *inputFile)
-{
-    const auto *file = qobject_cast<const QFile *>(inputFile);
-    return file ? QDir::toNativeSeparators(file->fileName()) : u"<stdin>"_s;
-}
-
 static QString msgSnippetNotFound(const QIODevice &inputFile,
                                   const QString &identifier)
 {
@@ -1208,7 +1241,7 @@ static QString fixLinkText(const QtXmlToSphinxLink *linkContext,
     }
     // For the language reference documentation, strip the module name.
     // Clear the link text if that matches the function/class/enumeration name.
-    const int lastSep = linktext.lastIndexOf(u"::");
+    const auto lastSep = linktext.lastIndexOf(u"::");
     if (lastSep != -1)
         linktext.remove(0, lastSep + 2);
     else
@@ -1238,80 +1271,13 @@ WebXmlTag QtXmlToSphinx::parentTag() const
     return index >= 0 ? m_tagStack.at(index) : WebXmlTag::Unknown;
 }
 
-// Copy images that are placed in a subdirectory "images" under the webxml files
-// by qdoc to a matching subdirectory under the "rst/PySide6/<module>" directory
-static bool copyImage(const QString &href, const QString &docDataDir,
-                      const QString &context, const QString &outputDir,
-                      const QLoggingCategory &lc, QString *errorMessage)
-{
-    const QChar slash = u'/';
-    const auto lastSlash = href.lastIndexOf(slash);
-    const QString imagePath = lastSlash != -1 ? href.left(lastSlash) : QString();
-    const QString imageFileName = lastSlash != -1 ? href.right(href.size() - lastSlash - 1) : href;
-    QFileInfo imageSource(docDataDir + slash + href);
-    if (!imageSource.exists()) {
-        QTextStream(errorMessage) << "Image " << href << " does not exist in "
-            << QDir::toNativeSeparators(docDataDir);
-        return false;
-    }
-    // Determine directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui".
-    // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or
-    // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint".
-    QString relativeTargetDir = context;
-    const auto lastDot = relativeTargetDir.lastIndexOf(u'.');
-    if (lastDot != -1)
-        relativeTargetDir.truncate(lastDot);
-    relativeTargetDir.replace(u'.', slash);
-    if (!imagePath.isEmpty())
-        relativeTargetDir += slash + imagePath;
-
-    const QString targetDir = outputDir + slash + relativeTargetDir;
-    const QString targetFileName = targetDir + slash + imageFileName;
-    if (QFileInfo::exists(targetFileName))
-        return true;
-    if (!QFileInfo::exists(targetDir)) {
-        const QDir outDir(outputDir);
-        if (!outDir.mkpath(relativeTargetDir)) {
-            QTextStream(errorMessage) << "Cannot create " << QDir::toNativeSeparators(relativeTargetDir)
-                << " under " << QDir::toNativeSeparators(outputDir);
-            return false;
-        }
-    }
-
-    QFile source(imageSource.absoluteFilePath());
-    if (!source.copy(targetFileName)) {
-        QTextStream(errorMessage) << "Cannot copy " << QDir::toNativeSeparators(source.fileName())
-            << " to " << QDir::toNativeSeparators(targetFileName) << ": "
-            << source.errorString();
-        return false;
-    }
-    qCDebug(lc).noquote().nospace() << __FUNCTION__ << " href=\""
-        << href << "\", context=\"" << context << "\", docDataDir=\""
-        << docDataDir << "\", outputDir=\"" << outputDir << "\", copied \""
-        << source.fileName() << "\"->\"" << targetFileName << '"';
-    return true;
-}
-
-bool QtXmlToSphinx::copyImage(const QString &href) const
-{
-    QString errorMessage;
-    const bool result =
-        ::copyImage(href, m_parameters.docDataDir, m_context,
-                    m_parameters.outputDirectory,
-                    m_generator->loggingCategory(),
-                    &errorMessage);
-    if (!result)
-        throw Exception(errorMessage);
-    return result;
-}
-
 void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader)
 {
     if (reader.tokenType() != QXmlStreamReader::StartElement)
         return;
     const QString href = reader.attributes().value(u"href"_s).toString();
-    if (copyImage(href))
-        m_output << ".. image:: " <<  href << "\n\n";
+    m_images.append({m_context, href});
+    m_output << ".. image:: " <<  href << "\n\n";
 }
 
 void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader)
@@ -1319,8 +1285,7 @@ void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader)
     if (reader.tokenType() != QXmlStreamReader::StartElement)
         return;
     const QString href = reader.attributes().value(u"href"_s).toString();
-    if (!copyImage(href))
-        return;
+    m_images.append({m_context, href});
     // Handle inline images by substitution references. Insert a unique tag
     // enclosed by '|' and define it further down. Determine tag from the base
     //file name with number.
index 398c5bc97aedf410567c8d96fad21ca21af0e5f4..1cd07a6a1199a4dfe1b352db20c2d449b70ebf43 100644 (file)
@@ -20,6 +20,7 @@ QT_END_NAMESPACE
 class QtXmlToSphinxDocGeneratorInterface;
 struct QtXmlToSphinxParameters;
 struct QtXmlToSphinxLink;
+struct QtXmlToSphinxImage;
 
 enum class WebXmlTag;
 
@@ -91,13 +92,15 @@ public:
     explicit QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
                            const QtXmlToSphinxParameters &parameters,
                            const QString& doc,
-                           const QString& context = QString());
+                           const QString& context);
+    explicit QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+                           const QtXmlToSphinxParameters &parameters,
+                           QIODevice &ioDevice,
+                           const QString& context);
     ~QtXmlToSphinx();
 
-    QString result() const
-    {
-        return m_result;
-    }
+    const QString &result() const { return m_result; }
+    const QList<QtXmlToSphinxImage> &images() const { return m_images; }
 
     static void stripPythonQualifiers(QString *s);
 
@@ -106,9 +109,14 @@ public:
                                QString *errorMessage);
 
 private:
+    explicit QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+                           const QtXmlToSphinxParameters &parameters,
+                           const QString& context = QString());
+
     using StringSharedPtr = std::shared_ptr<QString>;
 
     QString transform(const QString& doc);
+    QString transform(QXmlStreamReader& reader);
 
     void handleHeadingTag(QXmlStreamReader& reader);
     void handleParaTag(QXmlStreamReader& reader);
@@ -157,6 +165,7 @@ private:
     QStack<WebXmlTag> m_tagStack;
     TextStream m_output;
     QString m_result;
+    QList<QtXmlToSphinxImage> m_images;
 
     QStack<StringSharedPtr> m_buffers; // Maintain address stability since it used in TextStream
 
@@ -199,7 +208,6 @@ private:
     void pushOutputBuffer();
     QString popOutputBuffer();
     void writeTable(Table& table);
-    bool copyImage(const QString &href) const;
     void callHandler(WebXmlTag t, QXmlStreamReader &);
     void formatCurrentTable();
 };
index 16eefad832804046a985c31dce465862ec319b4c..dbef86f42512693b71d459720612f9987a48a856 100644 (file)
@@ -7,6 +7,17 @@
 #include <QtCore/QStringList>
 
 QT_FORWARD_DECLARE_CLASS(QLoggingCategory)
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+struct QtXmlToSphinxImage
+{
+    QString scope;
+    QString href;
+};
+
+QDebug operator<<(QDebug debug, const QtXmlToSphinxImage &i);
+
+using QtXmlToSphinxImages = QList<QtXmlToSphinxImage>;
 
 struct QtXmlToSphinxParameters
 {
index a7013aded14fc9a63966e73292ea19cfd5d47b3b..9eb1f2c13808427964d1f5afa0554b511851428b 100644 (file)
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
 
 #include "cppgenerator.h"
+#include "anystringview_helpers.h"
 #include "configurablescope.h"
 #include "generatorargument.h"
 #include "generatorstrings.h"
@@ -59,8 +60,11 @@ using namespace Qt::StringLiterals;
 static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr";
 
 static constexpr auto virtualMethodStaticReturnVar = "result"_L1;
+static constexpr auto initFuncPrefix = "init_"_L1;
 
 static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1;
+static constexpr auto enumConverterPythonType = "Enum"_L1;
+static constexpr auto flagsConverterPythonType = "Flag"_L1;
 static const char initInheritanceFunction[] = "initInheritance";
 
 static QString mangleName(QString name)
@@ -114,6 +118,9 @@ TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r)
     case CppGenerator::ErrorReturn::MinusOne:
         s << " -1";
         break;
+    case CppGenerator::ErrorReturn::NullPtr:
+        s << " nullptr";
+        break;
     case CppGenerator::ErrorReturn::Void:
         break;
     }
@@ -123,21 +130,79 @@ TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r)
 
 static constexpr auto converterVar = "converter"_L1;
 
-struct registerConverterName
+// Helper streamable class for generating code registering converters
+class registerConverterName
 {
+public:
+    enum Flag
+    {
+        Indirections = 0x1, // Also register "Type*", "Type&"
+        PartiallyQualifiedAliases = 0x2, // Also register "B" when passed "A::B"
+        TypeId = 0x4, // Use typeid().name() for the string passed in
+        Alias = 0x8 // This is an alias
+    };
+    Q_DECLARE_FLAGS(Flags, Flag)
+
     explicit registerConverterName(QAnyStringView typeName,
-                                   QAnyStringView varName = converterVar) :
-        m_typeName(typeName), m_varName(varName) {}
+                                   QAnyStringView varName = {},
+                                   Flags flags = {}) noexcept:
+        m_typeName(typeName), m_varName(varName), m_flags(flags) {}
+
+    void format(TextStream &s) const;
+
+    inline friend TextStream &operator<<(TextStream &s, const registerConverterName &r)
+    {
+        r.format(s);
+        return s;
+    }
+
+private:
+    static void formatEntry(TextStream &s, QAnyStringView typeName,
+                            QAnyStringView varName, Flags flags,
+                            const char *indirection = "");
 
     QAnyStringView m_typeName;
     QAnyStringView m_varName;
+    Flags m_flags;
 };
 
-TextStream &operator<<(TextStream &s, const registerConverterName &r)
+Q_DECLARE_OPERATORS_FOR_FLAGS(registerConverterName::Flags)
+
+void registerConverterName::formatEntry(TextStream &s, QAnyStringView typeName,
+                                        QAnyStringView varName, Flags flags,
+                                        const char *indirection)
 {
-    s << "Shiboken::Conversions::registerConverterName(" << r.m_varName
-        <<  ", \"" << r.m_typeName << "\");\n";
-    return s;
+    s << "Shiboken::Conversions::registerConverter"
+        << (flags.testFlag(Alias) ? "Alias" : "Name")
+        << '(' << varName << ", ";
+    if (flags.testFlag(TypeId))
+        s << "typeid(" << typeName << indirection << ").name()";
+    else
+        s << '"' << typeName << indirection << '"';
+    s << ");\n";
+}
+
+void registerConverterName::format(TextStream &s) const
+{
+    QAnyStringView typeName = m_typeName;
+    const QAnyStringView varName = m_varName.isEmpty() ? converterVar : m_varName;
+    auto flags = m_flags;
+
+    while (true) {
+        formatEntry(s, typeName, varName, flags);
+        if (flags.testFlag(Indirections)) {
+            formatEntry(s, typeName, varName, flags, "*");
+            formatEntry(s, typeName, varName, flags, "&");
+        }
+
+        if (!flags.testFlag(PartiallyQualifiedAliases))
+            break;
+        auto pos = asv_indexOf(typeName, "::");
+        if (pos < 0)
+            break;
+        typeName = typeName.sliced(pos + 2);
+        flags.setFlag(Alias);
+    }
 }
 
 // Protocol function name / function parameters / return type
@@ -152,11 +217,10 @@ using ProtocolEntries = QList<ProtocolEntry>;
 
 static bool contains(const ProtocolEntries &l, const QString &needle)
 {
-    for (const auto &m : l) {
-        if (m.name == needle)
-            return true;
-    }
-    return false;
+    return std::any_of(l.cbegin(), l.cend(),
+                       [&needle](const ProtocolEntry &e) {
+                           return e.name == needle;
+                       });
 }
 
 // Maps special function names to function parameters and return types
@@ -255,6 +319,22 @@ static const char includeQDebug[] =
 "#endif\n"
 "#include <QtCore/QDebug>\n";
 
+static QString compilerOptionOptimize()
+{
+    static QString result;
+    if (result.isEmpty()) {
+        const auto optimizations = CppGenerator::optimizations();
+        QTextStream str(&result);
+        str << "#define PYSIDE6_COMOPT_FULLNAME "
+            << (optimizations.testFlag(Generator::RemoveFullnameField) ? '1' : '0')
+            << "\n#define PYSIDE6_COMOPT_COMPRESS "
+            << (optimizations.testFlag(Generator::CompressSignatureStrings) ? '1' : '0')
+            << "\n// TODO: #define PYSIDE6_COMOPT_FOLDING "
+            << (optimizations.testFlag(Generator::FoldCommonTailCode) ? '1' : '0') << '\n';
+    }
+    return result;
+}
+
 QString CppGenerator::chopType(QString s)
 {
     if (s.endsWith(u"_Type"))
@@ -264,7 +344,7 @@ QString CppGenerator::chopType(QString s)
     return s;
 }
 
-static bool isStdSetterName(QString setterName, QString propertyName)
+static bool isStdSetterName(const QString &setterName, const QString &propertyName)
 {
    return setterName.size() == propertyName.size() + 3
           && setterName.startsWith(u"set")
@@ -426,7 +506,8 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class
         s << HeaderGenerator::protectedHackDefine;
 
     QByteArrayList cppIncludes{"typeinfo", "iterator", // for containers
-                               "cctype", "cstring"};
+                               "cctype", "cstring",
+                               "type_traits"}; // enum/underlying type
     // headers
     s << "// default includes\n";
     s << "#include <shiboken.h>\n";
@@ -435,6 +516,7 @@ void CppGenerator::generateIncludes(TextStream &s, const GeneratorContext &class
         cppIncludes << "iostream";
     }
 
+    s << '\n' << compilerOptionOptimize() << '\n';
     if (normalClass && usePySideExtensions()) {
         s << includeQDebug;
         if (metaClass->hasToStringCapability())
@@ -538,13 +620,11 @@ static void writeAddedTypeSignatures(TextStream &s, const ComplexTypeEntryCPtr &
 /// Function used to write the class generated binding code on the buffer
 /// \param s the output buffer
 /// \param classContext the pointer to metaclass information
-void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+void CppGenerator::generateClass(TextStream &s,
+                                 const QString & /* targetDir */,
+                                 const GeneratorContext &classContext,
+                                 QList<GeneratorContext> *)
 {
-    if (classContext.forSmartPointer()) {
-        generateSmartPointerClass(s, classContext);
-        return;
-    }
-
     s.setLanguage(TextStream::Language::Cpp);
     AbstractMetaClassCPtr metaClass = classContext.metaClass();
     const auto typeEntry = metaClass->typeEntry();
@@ -612,7 +692,8 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon
         }
 
         int maxOverrides = 0;
-        writeCacheResetNative(s, classContext);
+        if (useOverrideCaching(classContext.metaClass()))
+            writeCacheResetNative(s, classContext);
         for (const auto &func : metaClass->functions()) {
             const auto generation = functionGeneration(func);
             if (generation.testFlag(FunctionGenerationFlag::WrapperConstructor))
@@ -644,12 +725,13 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon
         if (overloads.isEmpty())
             continue;
 
-        const auto rfunc = overloads.constFirst();
+        const auto &rfunc = overloads.constFirst();
         OverloadData overloadData(overloads, api());
 
         if (rfunc->isConstructor()) {
             writeConstructorWrapper(s, overloadData, classContext);
-            writeSignatureInfo(signatureStream, overloadData);
+            // On constructors, we also generate the property initializers.
+            writeSignatureInfo(signatureStream, overloadData, true);
         }
         // call operators
         else if (rfunc->name() == u"operator()") {
@@ -751,7 +833,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon
 
     if (generateRichComparison(classContext)) {
         s << "// Rich comparison\n";
-        writeRichCompareFunction(s, classContext);
+        writeRichCompareFunction(s, signatureStream, classContext);
     }
 
     if (shouldGenerateGetSetList(metaClass)) {
@@ -804,7 +886,7 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon
     s << closeExternC;
 
     if (hasHashFunction(metaClass))
-        writeHashFunction(s, classContext);
+        writeHashFunction(s, signatureStream, classContext);
 
     // Write tp_traverse and tp_clear functions.
     writeTpTraverseFunction(s, metaClass);
@@ -859,18 +941,22 @@ void CppGenerator::writeConstructorNative(TextStream &s, const GeneratorContext
     const QString qualifiedName = classContext.wrapperName() + u"::"_s;
     s << functionSignature(func, qualifiedName, QString(),
                            OriginalTypeDescription | SkipDefaultValues);
-    s << " : ";
-    writeFunctionCall(s, func);
+    if (!func->arguments().isEmpty()) {
+        s << " : ";
+        writeFunctionCall(s, func);
+    }
     s << "\n{\n" << indent;
     if (wrapperDiagnostics())
         s << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n';
     const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
-    s << "resetPyMethodCache();\n";
-    writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionBeginning,
-                   TypeSystem::NativeCode, func, false, lastArg);
-    s << "// ... middle\n";
-    writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd,
-                   TypeSystem::NativeCode, func, false, lastArg);
+    const auto &codeSnips = func->injectedCodeSnips();
+    if (!codeSnips.isEmpty()) {
+        writeCodeSnips(s, codeSnips, TypeSystem::CodeSnipPositionBeginning,
+                       TypeSystem::NativeCode, func, false, lastArg);
+        s << "// ... middle\n";
+        writeCodeSnips(s, codeSnips, TypeSystem::CodeSnipPositionEnd,
+                       TypeSystem::NativeCode, func, false, lastArg);
+    }
     s << outdent << "}\n\n";
 }
 
@@ -996,7 +1082,7 @@ CppGenerator::VirtualMethodReturn
                 static const QRegularExpression regex("%(\\d+)"_L1);
                 Q_ASSERT(regex.isValid());
                 QString expr = argMod.replacedDefaultExpression();
-                for (int offset = 0; ; ) {
+                for (qsizetype offset = 0; ; ) {
                     const QRegularExpressionMatch match = regex.match(expr, offset);
                     if (!match.hasMatch())
                         break;
@@ -1109,7 +1195,7 @@ void CppGenerator::writeVirtualMethodNativeArgs(TextStream &s,
         << indent << argConversions.join(u",\n"_s) << outdent << "\n));\n";
 
     for (int index : std::as_const(invalidateArgs)) {
-        s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GET_ITEM(" << PYTHON_ARGS
+        s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GetItem(" << PYTHON_ARGS
             << ", " << index - 1 << ")) == 1;\n";
     }
 }
@@ -1224,9 +1310,8 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s,
     writeFuncNameVar(s, func, funcName);
     s << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR
         << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n"
-        << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent;
-    if (useOverrideCaching(func->ownerClass()))
-        s << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
+        << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent
+        << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
     writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
                               returnStatement.statement, true);
     s << outdent << "}\n\n"; //WS
@@ -1329,7 +1414,7 @@ void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s,
 
         for (int argIndex : std::as_const(invalidateArgs)) {
             s << "if (invalidateArg" << argIndex << ")\n" << indent
-                << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS
+                << "Shiboken::Object::invalidate(PyTuple_GetItem(" << PYTHON_ARGS
                 << ", " << (argIndex - 1) << "));\n" << outdent;
         }
         if (generateNewCall)
@@ -1337,7 +1422,8 @@ void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s,
 
         s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent
             << "// An error happened in python code!\n"
-            << "Shiboken::Errors::storeErrorOrPrint();\n"
+            << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\""
+            << func->ownerClass()->name() << "\", funcName);\n"
             << returnStatement.statement << "\n" << outdent
         << "}\n";
 
@@ -1576,18 +1662,65 @@ void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMeta
     c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) = value;\n";
 
     ConfigurableScope configScope(s, enumType);
-    writePythonToCppFunction(s, c.toString(), typeName, typeName);
+    writePythonToCppFunction(s, c.toString(), enumConverterPythonType, typeName);
 
     QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')';
-    writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck);
+    writeIsPythonConvertibleToCppFunction(s, enumConverterPythonType, typeName, pyTypeCheck);
 
     c.clear();
 
-    c << "const int castCppIn = int(*reinterpret_cast<const "
+    c << "using IntType = std::underlying_type_t<" << cppTypeName << ">;\n"
+         "const auto castCppIn = IntType(*reinterpret_cast<const "
         << cppTypeName << " *>(cppIn));\n" << "return "
         << "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn);\n";
-    writeCppToPythonFunction(s, c.toString(), typeName, typeName);
     s << '\n';
+    writeCppToPythonFunction(s, c.toString(), typeName, enumConverterPythonType);
+    s << '\n';
+
+    auto flags = enumType->flags();
+    if (!flags)
+        return;
+    QString flagsType = "QFlags<"_L1 + cppTypeName + u'>';
+
+    c.clear();
+    c << "const auto value = "<< flagsType << "::fromInt(Shiboken::Enum::getValue(pyIn));\n"
+        << "*reinterpret_cast<" << flagsType << " *>(cppOut) = value;\n";
+    writePythonToCppFunction(s, c.toString(), flagsConverterPythonType, typeName);
+
+    c.clear();
+    c << "const auto flags = *reinterpret_cast<const " << flagsType
+        << " *>(cppIn);\n"
+        << "return Shiboken::Enum::newItem(" << enumPythonType << ", flags.toInt());\n";
+    s << '\n';
+    writeCppToPythonFunction(s, c.toString(), typeName, flagsConverterPythonType);
+    s << '\n';
+}
+
+static void writePointerToPythonConverter(TextStream &c,
+                                          const AbstractMetaClassCPtr &metaClass,
+                                          const QString &typeName,
+                                          const QString &cpythonType)
+{
+    c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"
+        << "if (pyOut) {\n" << indent
+        << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
+        << "}\n";
+
+    const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction();
+    if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) {
+        c << "return Shiboken::Object::newObjectWithHeuristics("
+            << cpythonType << ", const_cast<void *>(cppIn), false);\n";
+        return;
+    }
+
+    c   << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
+const char *typeName = )";
+    if (nameFunc.isEmpty())
+        c << "typeid(*tCppIn).name();\n";
+    else
+        c << nameFunc << "(tCppIn);\n";
+    c << "return Shiboken::Object::newObjectForPointer("
+        << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n";
 }
 
 void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass,
@@ -1635,26 +1768,7 @@ void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClas
         c << "return PySide::getWrapperForQObject(reinterpret_cast<"
             << typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n";
     } else {
-        c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"
-            << "if (pyOut) {\n" << indent
-            << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
-            << "}\n"
-            << "bool exactType = false;\n"
-            << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
-const char *typeName = )";
-
-        const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction();
-        if (nameFunc.isEmpty())
-            c << "typeid(*tCppIn).name();\n";
-        else
-            c << nameFunc << "(tCppIn);\n";
-        c << R"(auto *sbkType = Shiboken::ObjectType::typeForTypeName(typeName);
-if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType))
-    exactType = true;
-)"
-            << "PyObject *result = Shiboken::Object::newObject(" << cpythonType
-            << R"(, const_cast<void *>(cppIn), false, exactType, typeName);
-return result;)";
+        writePointerToPythonConverter(c, metaClass, typeName, cpythonType);
     }
     std::swap(targetTypeName, sourceTypeName);
     writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName);
@@ -1835,53 +1949,36 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass
     }
     s << outdent << ");\n\n";
 
-    auto writeConversions = [&s](const QString &signature)
-    {
-        s << registerConverterName(signature) << registerConverterName(signature + u'*')
-            << registerConverterName(signature + u'&');
-    };
-
-    auto writeConversionsForType = [writeConversions](const QString &fullTypeName)
-    {
-        QStringList lst = fullTypeName.split(u"::"_s,
-                                               Qt::SkipEmptyParts);
-        while (!lst.isEmpty()) {
-            QString signature = lst.join(u"::"_s);
-            writeConversions(signature);
-            lst.removeFirst();
-        }
-    };
-
-
     if (!classContext.forSmartPointer()) {
-        writeConversionsForType(metaClass->qualifiedCppName());
+        s << registerConverterName(metaClass->qualifiedCppName(), {},
+                                   registerConverterName::Indirections
+                                   | registerConverterName::PartiallyQualifiedAliases);
     } else {
-        const QString &smartPointerType = classContext.preciseType().instantiations().at(0).cppSignature();
+        QString pointeeType = classContext.preciseType().instantiations().at(0).cppSignature();
         const QString &smartPointerName = classContext.preciseType().typeEntry()->name();
 
-        QStringList lst = smartPointerType.split(u"::"_s,
-                                               Qt::SkipEmptyParts);
-        while (!lst.isEmpty()) {
-            QString signature = lst.join(u"::"_s);
-            writeConversions(smartPointerName + u'<' + signature + u'>');
-            lst.removeFirst();
+        registerConverterName::Flags flags = registerConverterName::Indirections;
+        while (true) {
+            s << registerConverterName(smartPointerName + u'<' + pointeeType + u'>', {}, flags);
+            auto pos = pointeeType.indexOf("::"_L1);
+            if (pos < 0)
+                break;
+            pointeeType.remove(0, pos + 2);
+            flags.setFlag(registerConverterName::Alias);
         }
-
-        writeConversionsForType(smartPointerType);
     }
 
-    s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << m_gsp;
-    QString qualifiedCppNameInvocation;
+    QString qualifiedCppNameInvocation = m_gsp;
     if (!classContext.forSmartPointer())
-        qualifiedCppNameInvocation = metaClass->qualifiedCppName();
+        qualifiedCppNameInvocation += metaClass->qualifiedCppName();
     else
-        qualifiedCppNameInvocation = classContext.preciseType().cppSignature();
-
-    s << qualifiedCppNameInvocation << ").name());\n";
+        qualifiedCppNameInvocation += classContext.preciseType().cppSignature();
+    s << registerConverterName(qualifiedCppNameInvocation, {},
+                               registerConverterName::TypeId);
 
     if (classContext.useWrapper()) {
-        s << "Shiboken::Conversions::registerConverterName(converter, typeid("
-            << classContext.wrapperName() << ").name());\n";
+        s << registerConverterName(classContext.wrapperName(), {},
+                                   registerConverterName::TypeId);
     }
 
     if (!typeEntry->isValue() && !typeEntry->isSmartPointer())
@@ -1893,7 +1990,7 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass
     targetTypeName = sourceTypeName + u"_COPY"_s;
     QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
     QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
-    writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
+    writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
 
     // User provided implicit conversions.
 
@@ -1919,7 +2016,7 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass
         }
         QString toCpp = pythonToCppFunctionName(sourceType, targetType);
         QString isConv = convertibleToCppFunctionName(sourceType, targetType);
-        writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
+        writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
     }
 
     if (typeEntry->isValue()) {
@@ -1965,20 +2062,27 @@ bool CppGenerator::needsArgumentErrorHandling(const OverloadData &overloadData)
         && isQObject(rfunc->ownerClass());
 }
 
+static bool canAvoidFullname(const GeneratorContext &context)
+{
+    // PYSIDE-2701: Make fullname suppression configurable.
+    return Generator::optimizations().testFlag(Generator::RemoveFullnameField)
+           && context.hasClass();
+}
+
 void CppGenerator::writeMethodWrapperPreamble(TextStream &s,
                                               const OverloadData &overloadData,
                                               const GeneratorContext &context,
                                               ErrorReturn errorReturn)
 {
     const auto rfunc = overloadData.referenceFunction();
-    const auto ownerClass = rfunc->targetLangOwner();
-    Q_ASSERT(ownerClass == context.metaClass());
     int minArgs = overloadData.minArgs();
     int maxArgs = overloadData.maxArgs();
-    bool initPythonArguments;
+    bool initPythonArguments{};
 
     // If method is a constructor...
     if (rfunc->isConstructor()) {
+        const auto ownerClass = rfunc->targetLangOwner();
+        Q_ASSERT(ownerClass == context.metaClass());
         // Check if the right constructor was called.
         if (!ownerClass->hasPrivateDestructor()) {
             s << "if (Shiboken::Object::isUserType(self) && "
@@ -2016,9 +2120,11 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,
     if (needsArgumentErrorHandling(overloadData))
         s << "Shiboken::AutoDecRef errInfo{};\n";
 
-    s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true)
-        << "\";\nSBK_UNUSED(fullName)\n"
-        << "Shiboken::PythonContextMarker pcm;\n";
+    bool needsFullName = !canAvoidFullname(context);
+    if (needsFullName)
+        s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true)
+            << "\";\nSBK_UNUSED(fullName)\n";
+    s << "Shiboken::PythonContextMarker pcm;\n";
     // PYSIDE-2335: Mark blocking calls like `exec` or `run` as such.
     bool isBlockingFunction = rfunc->name() == u"exec"_s || rfunc->name() == u"exec_"_s
                               || rfunc->name() == u"run"_s;
@@ -2039,7 +2145,7 @@ void CppGenerator::writeMethodWrapperPreamble(TextStream &s,
             && !overloadData.pythonFunctionWrapperUsesListOfArguments()) {
             s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n";
         } else {
-            writeArgumentsInitializer(s, overloadData, errorReturn);
+            writeArgumentsInitializer(s, overloadData, context, errorReturn);
         }
     }
 }
@@ -2053,15 +2159,13 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov
     const auto metaClass = rfunc->ownerClass();
 
     s << "static int\n";
-    s << cpythonFunctionName(rfunc)
+    s << cpythonConstructorName(metaClass)
         << "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n" << indent;
     if (overloadData.maxArgs() == 0 || metaClass->isAbstract())
         s << sbkUnusedVariableCast("args");
     s << sbkUnusedVariableCast("kwds");
 
     const bool needsMetaObject = usePySideExtensions() && isQObject(metaClass);
-    if (needsMetaObject)
-        s << "const QMetaObject *metaObject;\n";
 
     s << "auto *sbkSelf = reinterpret_cast<SbkObject *>(self);\n";
 
@@ -2077,8 +2181,6 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov
             s << sbkUnusedVariableCast("sbkSelf")
                 << sbkUnusedVariableCast("type")
                 << sbkUnusedVariableCast("myType");
-            if (needsMetaObject)
-                s << sbkUnusedVariableCast("metaObject");
             s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\""
                 << metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent
                 << "}\n\n";
@@ -2110,13 +2212,14 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov
     s << '\n';
 
     if (overloadData.maxArgs() > 0)
-        writeOverloadedFunctionDecisor(s, overloadData, errorReturn);
+        writeOverloadedFunctionDecisor(s, overloadData, classContext, errorReturn);
 
     // Handles Python Multiple Inheritance
     QString pre = needsMetaObject ? u"bool usesPyMI = "_s : u""_s;
     s << "\n// PyMI support\n"
-        << pre << "Shiboken::callInheritedInit(self, args, kwds, fullName);\n"
-        << "if (" << shibokenErrorsOccurred << ")\n"
+        << pre << "Shiboken::callInheritedInit(self, args, kwds, "
+        << (canAvoidFullname(classContext) ? typeInitStruct(classContext) : "fullName"_L1)
+        << ");\nif (" << shibokenErrorsOccurred << ")\n"
         << indent << errorReturn << outdent << "\n";
 
     writeFunctionCalls(s, overloadData, classContext, errorReturn);
@@ -2131,8 +2234,8 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov
         << "}\n";
     if (overloadData.maxArgs() > 0)
         s << "if (cptr == nullptr)\n" << indent
-            << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n"
-            << outdent;
+            << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+            << ";\n\n" << outdent;
 
     s << "Shiboken::Object::setValidCpp(sbkSelf, true);\n";
     // If the created C++ object has a C++ wrapper the ownership is assigned to Python
@@ -2152,11 +2255,11 @@ void CppGenerator::writeConstructorWrapper(TextStream &s, const OverloadData &ov
     if (needsMetaObject) {
         s << "\n// QObject setup\n"
             << "PySide::Signal::updateSourceObject(self);\n"
-            << "metaObject = cptr->metaObject(); // <- init python qt properties\n"
+            << "const auto *metaObject = cptr->metaObject(); // <- init python qt properties\n"
             << "if (!errInfo.isNull() && PyDict_Check(errInfo.object())) {\n" << indent
                 << "if (!PySide::fillQtProperties(self, metaObject, errInfo, usesPyMI))\n" << indent
-                    << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
-                    << outdent << outdent
+                    << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+                    << ";\n" << outdent << outdent
             << "};\n";
     }
 
@@ -2258,12 +2361,12 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa
             << "// Do not enter here if other object has implemented a reverse operator.\n"
             << "if (" << PYTHON_RETURN_VAR << " == nullptr) {\n" << indent;
         if (maxArgs > 0)
-            writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default);
+            writeOverloadedFunctionDecisor(s, overloadData, classContext, ErrorReturn::Default);
         writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default);
         s  << outdent << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n";
     } else { // binary shift operator
         if (maxArgs > 0)
-            writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default);
+            writeOverloadedFunctionDecisor(s, overloadData, classContext, ErrorReturn::Default);
         writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default);
     }
 
@@ -2286,10 +2389,11 @@ void CppGenerator::writeMethodWrapper(TextStream &s, const OverloadData &overloa
 }
 
 void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData,
+                                             const GeneratorContext &classContext,
                                              ErrorReturn errorReturn)
 {
     const auto rfunc = overloadData.referenceFunction();
-    s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs");
+    s << "PyTuple_Size(args);\n" << sbkUnusedVariableCast("numArgs");
 
     int minArgs = overloadData.minArgs();
     int maxArgs = overloadData.maxArgs();
@@ -2326,7 +2430,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &
         s << "errInfo.reset(Shiboken::checkInvalidArgumentCount(numArgs, "
             <<  minArgs << ", " << maxArgs << "));\n"
             << "if (!errInfo.isNull())\n" << indent
-            << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+            << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn) << ";\n"
             << outdent;
     }
 
@@ -2339,7 +2443,7 @@ void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &
             s << "numArgs == " << invalidArgsLength.at(i);
         }
         s << ")\n" << indent
-            << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+            << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn) << ";\n"
             << outdent;
     }
     s  << '\n';
@@ -2459,18 +2563,28 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s,
 }
 
 QString CppGenerator::returnErrorWrongArguments(const OverloadData &overloadData,
-                                                       ErrorReturn errorReturn)
+                                                const GeneratorContext &context,
+                                                ErrorReturn errorReturn)
 {
+    Q_UNUSED(context);
     const auto rfunc = overloadData.referenceFunction();
+    QString exprRest;
+    if (canAvoidFullname(context)) {
+        const QString &name = rfunc->isConstructor() ? "__init__"_L1 : rfunc->name();
+        exprRest = ", \""_L1 + name + "\", errInfo, "_L1 + typeInitStruct(context) + ")"_L1;
+    } else {
+        exprRest = ", fullName, errInfo)"_L1;
+    }
     QString argsVar = overloadData.pythonFunctionWrapperUsesListOfArguments()
         ? u"args"_s : PYTHON_ARG;
     switch (errorReturn) {
     case ErrorReturn::Default:
-        return u"Shiboken::returnWrongArguments("_s + argsVar + u", fullName, errInfo)"_s;
+    case ErrorReturn::NullPtr:
+        return u"Shiboken::returnWrongArguments("_s + argsVar + exprRest;
     case ErrorReturn::Zero:
-        return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + u", fullName, errInfo)"_s;
+        return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + exprRest;
     case ErrorReturn::MinusOne:
-        return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + u", fullName, errInfo)"_s;
+        return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + exprRest;
     case ErrorReturn::Void:
         Q_ASSERT(false);
     }
@@ -2610,7 +2724,7 @@ void CppGenerator::writeTypeCheck(TextStream &s,
     // This condition trusts that the OverloadData object will arrange for
     // PyLong type to come after the more precise numeric types (e.g. float and bool)
     AbstractMetaType argType = overloadData->modifiedArgType();
-    if (auto viewOn = argType.viewOn())
+    if (const auto *viewOn = argType.viewOn())
         argType = *viewOn;
     const bool numberType = numericTypes.size() == 1 || ShibokenGenerator::isPyInt(argType);
     bool rejectNull =
@@ -2664,7 +2778,7 @@ static inline QString arrayHandleType(const AbstractMetaTypeList &nestedArrayTyp
             + QString::number(nestedArrayTypes.constFirst().arrayElementCount())
             + u'>';
     }
-    return QString();
+    return {};
 }
 
 // Helper to write argument initialization code for a function argument
@@ -2864,13 +2978,14 @@ void CppGenerator::writeNoneReturn(TextStream &s, const AbstractMetaFunctionCPtr
 
 void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s,
                                                   const OverloadData &overloadData,
+                                                  const GeneratorContext &classContext,
                                                   ErrorReturn errorReturn) const
 {
     s << "// Overloaded function decisor\n";
     const auto rfunc = overloadData.referenceFunction();
     const AbstractMetaFunctionCList &functionOverloads = overloadData.overloads();
     for (qsizetype i = 0; i < functionOverloads.size(); ++i) {
-        const auto func = functionOverloads.at(i);
+        const auto &func = functionOverloads.at(i);
         s << "// " << i << ": ";
         if (func->isStatic())
             s << "static ";
@@ -2892,8 +3007,8 @@ void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s,
 
     s << "// Function signature not found.\n"
         << "if (overloadId == -1)\n" << indent
-            << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n"
-            << outdent;
+            << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+            << ";\n\n" << outdent;
 }
 
 void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s,
@@ -3067,7 +3182,7 @@ void CppGenerator::writeFunctionCalls(TextStream &s, const OverloadData &overloa
                                 errorReturn);
     } else {
         for (qsizetype i = 0; i < overloads.size(); ++i) {
-            const auto func = overloads.at(i);
+            const auto &func = overloads.at(i);
             s << "case " << i << ": // " << func->signature() << "\n{\n" << indent;
             writeSingleFunctionCall(s, overloadData, func, context, errorReturn);
             s << "break;\n" << outdent << "}\n";
@@ -3109,7 +3224,7 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s,
     const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments();
 
     // Handle named arguments.
-    writeNamedArgumentResolution(s, func, usePyArgs, overloadData, errorReturn);
+    writeNamedArgumentResolution(s, func, usePyArgs, overloadData, context, errorReturn);
 
     bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func);
     bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc;
@@ -3156,14 +3271,17 @@ void CppGenerator::writeSingleFunctionCall(TextStream &s,
 
     int numRemovedArgs = OverloadData::numberOfRemovedArguments(func);
 
-    s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent;
+    const bool hasArguments = !func->arguments().isEmpty();
+    if (hasArguments) // Argument conversion error?
+        s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent;
     writeMethodCall(s, func, context,
                     overloadData.pythonFunctionWrapperUsesListOfArguments(),
                     func->arguments().size() - numRemovedArgs, indirections, errorReturn);
 
     if (!func->isConstructor())
         writeNoneReturn(s, func, overloadData.hasNonVoidReturnType());
-    s << outdent << "}\n";
+    if (hasArguments)
+        s << outdent << "}\n";
 }
 
 QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName)
@@ -3424,7 +3542,7 @@ void CppGenerator::writePythonToCppConversionFunction(TextStream &s,
         // Containers of opaque containers are not handled here.
         const auto generatorArg = GeneratorArgument::fromMetaType(type);
         if (generatorArg.indirections > 0 && !type.generateOpaqueContainer()) {
-            for (int pos = 0; ; ) {
+            for (qsizetype pos = 0; ; ) {
                 const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos);
                 if (!match.hasMatch())
                     break;
@@ -3505,6 +3623,7 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s,
                                                 const AbstractMetaFunctionCPtr &func,
                                                 bool usePyArgs,
                                                 const OverloadData &overloadData,
+                                                const GeneratorContext &classContext,
                                                 ErrorReturn errorReturn)
 {
     const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func);
@@ -3517,8 +3636,8 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s,
             s << "if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n" << indent
                 << "errInfo.reset(kwds);\n"
                 << "Py_INCREF(errInfo.object());\n"
-                << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
-                << outdent << "}\n";
+                << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+                << ";\n" << outdent << "}\n";
         }
         return;
     }
@@ -3541,14 +3660,14 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s,
             << "if (value != nullptr && " << pyArgName << " != nullptr ) {\n"
             << indent << "errInfo.reset(" << pyKeyName << ");\n"
             << "Py_INCREF(errInfo.object());\n"
-            << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
-            << outdent << "}\nif (value != nullptr) {\n" << indent
+            << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+            << ";\n" << outdent << "}\nif (value != nullptr) {\n" << indent
             << pyArgName << " = value;\nif (!";
         const auto &type = arg.modifiedType();
         writeTypeCheck(s, type, pyArgName, isNumber(type.typeEntry()), {});
         s << ")\n" << indent
-            << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
-            << outdent << outdent
+            << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+            << ";\n" << outdent << outdent
             << "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n"
             << outdent << "}\n";
     }
@@ -3558,10 +3677,12 @@ void CppGenerator::writeNamedArgumentResolution(TextStream &s,
     // until extra keyword signals and properties are handled.
     s << "if (PyDict_Size(kwds_dup) > 0) {\n" << indent
         << "errInfo.reset(kwds_dup.release());\n";
-    if (!(func->isConstructor() && isQObject(func->ownerClass())))
-        s << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n";
-    else
+    if (!(func->isConstructor() && isQObject(func->ownerClass()))) {
+        s << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+            << ";\n";
+    } else {
         s << "// fall through to handle extra keyword signals and properties\n";
+    }
     s << outdent << "}\n"
         << outdent << "}\n";
 }
@@ -4163,18 +4284,6 @@ void CppGenerator::writePrimitiveConverterInitialization(TextStream &s,
     writeCustomConverterRegister(s, customConversion, converter);
 }
 
-static void registerConverterInScopes(TextStream &s, QStringView signature,
-                                      QAnyStringView varName = converterVar)
-{
-    while (true) {
-        s << registerConverterName(signature, varName);
-        const auto qualifierPos = signature.indexOf("::"_L1);
-        if (qualifierPos == -1)
-            break;
-        signature = signature.sliced(qualifierPos + 2);
-    }
-}
-
 void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum)
 {
     if (metaEnum.isPrivate() || metaEnum.isAnonymous())
@@ -4190,19 +4299,31 @@ void CppGenerator::writeEnumConverterInitialization(TextStream &s, const Abstrac
     const QString typeName = fixedCppTypeName(enumType);
     s << "SbkConverter *converter = Shiboken::Conversions::createConverter("
         << enumPythonVar << ',' << '\n' << indent
-        << cppToPythonFunctionName(typeName, typeName) << ");\n" << outdent;
+        << cppToPythonFunctionName(typeName, enumConverterPythonType) << ");\n" << outdent;
 
-    const QString toCpp = pythonToCppFunctionName(typeName, typeName);
-    const QString isConv = convertibleToCppFunctionName(typeName, typeName);
+    QString toCpp = pythonToCppFunctionName(enumConverterPythonType, typeName);
+    const QString isConv = convertibleToCppFunctionName(enumConverterPythonType, typeName);
     writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
-    s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar
-        << ", converter);\n";
-
-    registerConverterInScopes(s, enumType->qualifiedCppName());
-    if (auto flags = enumType->flags())
+    s << registerConverterName(enumType->qualifiedCppName(), {},
+                               registerConverterName::PartiallyQualifiedAliases);
+
+    QString flagsConverter = "nullptr"_L1;
+    if (auto flags = enumType->flags()) {
+        flagsConverter = "flagsConverter"_L1;
+        toCpp = pythonToCppFunctionName(flagsConverterPythonType, typeName);
+        s << "SbkConverter *" << flagsConverter
+            << " = Shiboken::Conversions::createConverter("
+            << enumPythonVar << ',' << '\n' << indent
+            << cppToPythonFunctionName(typeName, flagsConverterPythonType)
+            << ");\n" << outdent;
+        writeAddPythonToCppConversion(s, flagsConverter, toCpp, isConv);
         s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n"
-            << registerConverterName(flags->name()) // QMetaType
-            << registerConverterName(flags->originalName()); // Signals with flags
+            << registerConverterName(flags->name(), flagsConverter) // QMetaType
+            << registerConverterName(flags->originalName(), flagsConverter); // Signals with flags
+    }
+
+    s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar
+        << ", converter, " << flagsConverter << ");\n";
 
     s << outdent << "}\n";
 }
@@ -4257,10 +4378,24 @@ QString CppGenerator::writeContainerConverterInitialization(TextStream &s,
     return converter;
 }
 
+QString CppGenerator::typeInitStructHelper(const TypeEntryCPtr &te, const QString &varName)
+{
+    return cppApiVariableName(te->targetLangPackage()) + u'[' + varName + u']';
+}
+
+QString CppGenerator::typeInitStruct(const GeneratorContext &context)
+{
+    Q_ASSERT(context.hasClass());
+    if (context.forSmartPointer()) {
+        auto te = context.preciseType().typeEntry();
+        return typeInitStructHelper(te, getTypeIndexVariableName(context.preciseType()));
+    }
+    return typeInitStruct(context.metaClass()->typeEntry());
+}
+
 QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te)
 {
-    return cppApiVariableName(te->targetLangPackage()) + u'['
-        + getTypeIndexVariableName(te) + u']';
+    return typeInitStructHelper(te, getTypeIndexVariableName(te));
 }
 
 void CppGenerator::writeExtendedConverterInitialization(TextStream &s,
@@ -4287,12 +4422,11 @@ QString CppGenerator::multipleInheritanceInitializerFunctionName(const AbstractM
 
 bool CppGenerator::supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass)
 {
-    for (const auto &m : mappingProtocols()) {
-        if (metaClass->hasFunction(m.name))
-            return true;
-    }
-
-    return false;
+    const auto &protocols = mappingProtocols();
+    return  std::any_of(protocols.cbegin(), protocols.cend(),
+                        [&metaClass](const ProtocolEntry &e) {
+                            return metaClass->hasFunction(e.name);
+                        });
 }
 
 bool CppGenerator::supportsNumberProtocol(const AbstractMetaClassCPtr &metaClass)
@@ -4385,7 +4519,7 @@ void CppGenerator::writeClassDefinition(TextStream &s,
         tp_dealloc = isQApp
             ? u"&SbkDeallocQAppWrapper"_s : u"&SbkDeallocWrapper"_s;
         if (!onlyPrivCtor && !ctors.isEmpty())
-            tp_init = cpythonFunctionName(ctors.constFirst());
+            tp_init = cpythonConstructorName(metaClass);
     }
 
     const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass);
@@ -4553,7 +4687,7 @@ void CppGenerator::writeSequenceMethods(TextStream &s,
         const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
         writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny,
                        TypeSystem::TargetLangCode, func, false /* uses PyArgs */, lastArg);
-        s<< outdent << "}\n\n";
+        s << outdent << "}\n\n";
     }
 
     if (!injectedCode)
@@ -4672,7 +4806,7 @@ void CppGenerator::writeTypeAsNumberDefinition(TextStream &s, const AbstractMeta
 
     const QList<AbstractMetaFunctionCList> opOverloads = numberProtocolOperators(metaClass);
     for (const auto &opOverload : opOverloads) {
-        const auto rfunc = opOverload.at(0);
+        const auto &rfunc = opOverload.at(0);
         QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
         nb[opName] = cpythonFunctionName(rfunc);
     }
@@ -4725,7 +4859,7 @@ QString CppGenerator::writeCopyFunction(TextStream &s,
     const QString className = chopType(cpythonTypeName(metaClass));
     const QString funcName = className + u"__copy__"_s;
 
-    signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n";
+    signatureStream << fullPythonClassName(metaClass) << ".__copy__(self)->typing.Self\n";
     definitionStream << PyMethodDefEntry{u"__copy__"_s, funcName, {"METH_NOARGS"_ba}, {}}
                      << ",\n";
 
@@ -4949,7 +5083,7 @@ void CppGenerator::writeRichCompareFunctionHeader(TextStream &s,
         << sbkUnusedVariableCast(PYTHON_TO_CPP_VAR) << '\n';
 }
 
-void CppGenerator::writeRichCompareFunction(TextStream &s,
+void CppGenerator::writeRichCompareFunction(TextStream &s, TextStream &t,
                                             const GeneratorContext &context) const
 {
     const auto metaClass = context.metaClass();
@@ -4975,6 +5109,7 @@ void CppGenerator::writeRichCompareFunction(TextStream &s,
 
         bool first = true;
         OverloadData overloadData(overloads, api());
+        writeSignatureInfo(t, overloadData);
         const OverloadDataList &nextOverloads = overloadData.children();
         for (const auto &od : nextOverloads) {
             const auto func = od->referenceFunction();
@@ -5108,7 +5243,7 @@ QString CppGenerator::pythonSignature(const AbstractMetaType &type) const
 }
 
 // Format the type signature of a function parameter
-QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const
+QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg, bool implicitConversions) const
 {
     QString result;
     QTextStream s(&result);
@@ -5122,8 +5257,8 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const
 
     // Implicit conversions (C++): Check for converting constructors
     // "QColor(Qt::GlobalColor)" or conversion operators
-    const AbstractMetaFunctionCList conversions =
-        api().implicitConversions(metaType);
+    const AbstractMetaFunctionCList conversions = implicitConversions ?
+        api().implicitConversions(metaType) : AbstractMetaFunctionCList{};
     for (const auto &f : conversions) {
         if (f->isConstructor() && !f->arguments().isEmpty()) {
             // PYSIDE-2712: modified types from converting constructors are not always correct
@@ -5150,12 +5285,13 @@ QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const
     return result;
 }
 
-void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloadData) const
+void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloadData,
+                                      bool useProperties) const
 {
     const auto rfunc = overloadData.referenceFunction();
     QString funcName = fullPythonFunctionName(rfunc, false);
 
-    int idx = overloadData.overloads().length() - 1;
+    auto idx = overloadData.overloads().length() - 1;
     bool multiple = idx > 0;
 
     for (const auto &f : overloadData.overloads()) {
@@ -5164,6 +5300,7 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa
         // Toplevel functions like `PySide6.QtCore.QEnum` are always self-less.
         if (!(f->isStatic()) && f->ownerClass())
             args << PYTHON_SELF_VAR;
+        const bool implicitConversions = f->functionType() != AbstractMetaFunction::CopyConstructorFunction;
         const auto &arguments = f->arguments();
         for (qsizetype i = 0, size = arguments.size(); i < size; ++i) {
             const auto n = i + 1;
@@ -5171,7 +5308,7 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa
             if (!f->argumentRemoved(n)) {
                 QString t = f->pyiTypeReplaced(n);
                 if (t.isEmpty()) {
-                    t = signatureParameter(arg);
+                    t = signatureParameter(arg, implicitConversions);
                 } else {
                     t.prepend(u':');
                     t.prepend(arg.name());
@@ -5182,6 +5319,33 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa
                 args.append(t);
             }
         }
+        // PYSIDE-1846: In a constructor, provide all properties as keyword-only parameters.
+        const auto &metaClass = rfunc->ownerClass();
+        if (useProperties && !metaClass->propertySpecs().isEmpty()) {
+            args << "*:KeywordOnly=None"_L1;
+            for (const auto &spec : metaClass->propertySpecs()) {
+                auto typeEntry = spec.typeEntry();
+                QString text;
+                if (typeEntry->isFlags()) {
+                    const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(typeEntry);
+                    text = fte->originator()->qualifiedTargetLangName();
+                } else {
+                    text = typeEntry->qualifiedCppName();
+                }
+                auto &inst = spec.type().instantiations();
+                if (!inst.isEmpty()) {
+                    text += u'[';
+                    for (qsizetype i = 0, size = inst.size(); i < size; ++i) {
+                        if (i > 0)
+                            text += u", "_s;
+                        text += pythonSignature(inst.at(i));
+                    }
+                    text += u']';
+                }
+                QString entry = spec.name() + u':' + text.replace(u"::"_s, u"."_s) + "=None"_L1;
+                args.append(entry);
+            }
+        }
 
         // mark the multiple signatures as such, to make it easier to generate different code
         if (multiple)
@@ -5344,7 +5508,7 @@ bool CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum
 
     QString enumVarTypeObj = cpythonTypeNameExtSet(enumTypeEntry);
     if (!cppEnum.isAnonymous()) {
-        int packageLevel = packageName().count(u'.') + 1;
+        auto packageLevel = packageName().count(u'.') + 1;
         s << "EType = Shiboken::Enum::"
             << "createPythonEnum"
             << '(' << enclosingObjectVariable << ",\n" << indent
@@ -5378,7 +5542,7 @@ void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaCl
             continue;
         const AbstractMetaArgumentList &arguments = cppSignal->arguments();
         for (const AbstractMetaArgument &arg : arguments) {
-            AbstractMetaType metaType = arg.type();
+            const AbstractMetaType &metaType = arg.type();
             const QByteArray origType =
                 QMetaObject::normalizedType(qPrintable(metaType.originalTypeDescription()));
             const QByteArray cppSig =
@@ -5418,6 +5582,26 @@ QString CppGenerator::getInitFunctionName(const GeneratorContext &context)
     return getSimpleClassInitFunctionName(context.metaClass());
 }
 
+static QString formatHex(const QByteArray &data)
+{
+    QString result;
+    QTextStream str(&result);
+    const auto oldFieldWidth = str.fieldWidth();
+    str.setIntegerBase(16);
+    str.setPadChar(u'0');
+    for (qsizetype c = 0, size = data.size(); c < size; ++c) {
+        if (c != 0)
+            str << (c % 16 == 0 ? ",\n" : ", ");
+        str << "0x";
+        str.setFieldWidth(2);
+        str << uchar(data.at(c));
+        str.setFieldWidth(oldFieldWidth);
+    }
+    return result;
+}
+
+static constexpr int compressionLevel = 9;      // almost no effect. Most blocks are small.
+
 void CppGenerator::writeSignatureStrings(TextStream &s,
                                          const QString &signatures,
                                          const QString &arrayName,
@@ -5425,16 +5609,36 @@ void CppGenerator::writeSignatureStrings(TextStream &s,
 {
     s << "// The signatures string for the " << comment << ".\n"
         << "// Multiple signatures have their index \"n:\" in front.\n"
+        << "#if PYSIDE6_COMOPT_COMPRESS == 0\n"
         << "static const char *" << arrayName << "_SignatureStrings[] = {\n" << indent;
     const auto lines = QStringView{signatures}.split(u'\n', Qt::SkipEmptyParts);
+
+    QByteArray buffer;
+    buffer.reserve(signatures.size());
     for (auto line : lines) {
         // must anything be escaped?
         if (line.contains(u'"') || line.contains(u'\\'))
             s << "R\"CPP(" << line << ")CPP\",\n";
         else
             s << '"' << line << "\",\n";
+        // collect also for compression
+        buffer += line.toLatin1();
+        buffer += '\n';
     }
-    s << NULL_PTR << "}; // Sentinel\n" << outdent << '\n';
+
+    // The Qt compressor has a 4-byte big endian length in front.
+    QByteArray compressed = qCompress(buffer, compressionLevel).sliced(4);
+    const auto binSize = compressed.size();
+    const auto adjustSize = qMax(binSize, 1);   // Windows
+    if (compressed.isEmpty())
+        compressed.append('\0');
+
+    s << NULL_PTR << "}; // Sentinel\n" << outdent
+        << "#else\n"
+        << "static constexpr size_t " << arrayName << "_SignatureByteSize = " << binSize << ";\n"
+        << "static constexpr uint8_t " << arrayName << "_SignatureBytes[" << adjustSize << "] = {\n"
+        << indent << formatHex(compressed) << outdent << "\n};\n"
+        << "#endif\n\n";
 }
 
 // Return the class name for which to invoke the destructor
@@ -5598,7 +5802,12 @@ void CppGenerator::writeClassRegister(TextStream &s,
 
     s << outdent << ");\nauto *pyType = " << pyTypeName << "; // references "
         << typePtr << "\n"
-        << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n";
+        << outdent << "#if PYSIDE6_COMOPT_COMPRESS == 0\n" << indent
+        << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n"
+        << outdent << "#else\n" << indent
+        << "InitSignatureBytes(pyType, " << initFunctionName << "_SignatureBytes, "
+        << initFunctionName << "_SignatureByteSize);\n"
+        << outdent << "#endif\n" << indent;
 
     if (usePySideExtensions() && !classContext.forSmartPointer())
         s << "SbkObjectType_SetPropertyStrings(pyType, "
@@ -5609,6 +5818,9 @@ void CppGenerator::writeClassRegister(TextStream &s,
     writeConverterRegister(s, metaClass, classContext);
     s << '\n';
 
+    if (classContext.forSmartPointer())
+        writeSmartPointerConverterInitialization(s, classContext.preciseType());
+
     // class inject-code target/beginning
     if (!classTypeEntry->codeSnips().isEmpty()) {
         writeClassCodeSnips(s, classTypeEntry->codeSnips(),
@@ -5842,7 +6054,7 @@ void CppGenerator::writeTypeDiscoveryFunction(TextStream &s,
     } else if (metaClass->isPolymorphic()) {
         const auto &ancestors = metaClass->allTypeSystemAncestors();
         for (const auto &ancestor : ancestors) {
-            if (ancestor->baseClass())
+            if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase())
                 continue;
             if (ancestor->isPolymorphic()) {
                 s << "if (instanceType == Shiboken::SbkType< " << m_gsp
@@ -6044,16 +6256,20 @@ void CppGenerator::writeNbBoolFunction(const GeneratorContext &context,
 
 // Write declaration and invocation of the init function for the module init
 // function.
-void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr,
-                                 const QString &initFunctionName,
-                                 const TypeEntryCPtr &enclosingEntry,
-                                 const QString &pythonName, bool lazy)
+static void writeInitFuncDecl(TextStream &declStr,
+                              const QString &functionName)
 {
-    const QString functionName = "init_"_L1 + initFunctionName;
-    const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
-    declStr << "PyTypeObject *" << functionName << "(PyObject *"
-        << (hasParent ? "enclosingClass" : "module") << ");\n";
+    declStr << "PyTypeObject *" << functionName  << "(PyObject *enclosing);\n";
+}
 
+// Write declaration and invocation of the init function for the module init
+// function.
+void CppGenerator::writeInitFuncCall(TextStream &callStr,
+                                     const QString &functionName,
+                                     const TypeEntryCPtr &enclosingEntry,
+                                     const QString &pythonName, bool lazy)
+{
+    const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
     if (!lazy) {
         const QString enclosing = hasParent
             ? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')'
@@ -6062,18 +6278,14 @@ void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr,
     } else if (hasParent) {
         const QString &enclosingName = enclosingEntry->name();
         const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts);
+        const QString namePathPrefix = enclosingEntry->name().replace("::"_L1, "."_L1);
         callStr << "Shiboken::Module::AddTypeCreationFunction("
-            << "module, \"" << pythonName << "\", " << functionName << ", \"";
-        for (qsizetype i = 0; i < parts.size(); ++i) {
-            if (i > 0)
-               callStr << "\", \"";
-            callStr << parts.at(i);
-        }
-        callStr << "\");\n";
+            << "module, \"" << parts[0] << "\", "
+            << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n";
     } else {
         callStr << "Shiboken::Module::AddTypeCreationFunction("
             << "module, \"" << pythonName << "\", "
-            << "init_" << initFunctionName << ");\n";
+            << functionName << ");\n";
     }
 }
 
@@ -6130,9 +6342,10 @@ bool CppGenerator::finishGeneration()
                 s_classInitDecl << te->configCondition() << '\n';
                 s_classPythonDefines << te->configCondition() << '\n';
             }
-            writeInitFunc(s_classInitDecl, s_classPythonDefines,
-                          getSimpleClassInitFunctionName(cls),
-                          targetLangEnclosingEntry(te), cls->name());
+            const QString initFunc = initFuncPrefix + getSimpleClassInitFunctionName(cls);
+            writeInitFuncDecl(s_classInitDecl, initFunc);
+            writeInitFuncCall(s_classPythonDefines, initFunc,
+                              targetLangEnclosingEntry(te), cls->name());
             if (cls->hasStaticFields()) {
                 s_classInitDecl << "PyTypeObject *"
                     << getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n";
@@ -6151,13 +6364,14 @@ bool CppGenerator::finishGeneration()
         const auto enclosingClass = context.metaClass()->enclosingClass();
         auto enclosingTypeEntry = targetLangEnclosingEntry(smp.specialized->typeEntry());
 
-        writeInitFunc(s_classInitDecl, s_classPythonDefines,
-                      getInitFunctionName(context),
-                      enclosingTypeEntry, smp.type.name());
+        const QString initFunc = initFuncPrefix + getInitFunctionName(context);
+        writeInitFuncDecl(s_classInitDecl, initFunc);
+        writeInitFuncCall(s_classPythonDefines,
+                          initFunc, enclosingTypeEntry, smp.specialized->name());
         includes.insert(smp.type.instantiations().constFirst().typeEntry()->include());
     }
 
-    for (auto &instantiatedContainer : api().instantiatedContainers()) {
+    for (const auto &instantiatedContainer : api().instantiatedContainers()) {
         includes.insert(instantiatedContainer.typeEntry()->include());
         for (const auto &inst : instantiatedContainer.instantiations())
             includes.insert(inst.typeEntry()->include());
@@ -6165,7 +6379,7 @@ bool CppGenerator::finishGeneration()
 
     const ExtendedConverterData extendedConverters = getExtendedConverters();
     for (auto it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
-        TypeEntryCPtr te = it.key();
+        const TypeEntryCPtr &te = it.key();
         includes.insert(te->include());
         for (const auto &metaClass : it.value())
             includes.insert(metaClass->typeEntry()->include());
@@ -6196,6 +6410,7 @@ bool CppGenerator::finishGeneration()
     if (!api().instantiatedContainers().isEmpty())
         s << "#include <sbkcontainer.h>\n#include <sbkstaticstrings.h>\n";
 
+    s << '\n' << compilerOptionOptimize() << '\n';
     if (usePySideExtensions()) {
         s << includeQDebug;
         s << R"(#include <pysidecleanup.h>
@@ -6308,7 +6523,7 @@ bool CppGenerator::finishGeneration()
     if (!extendedConverters.isEmpty()) {
         s  << '\n' << "// Extended Converters.\n\n";
         for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
-            TypeEntryCPtr externalType = it.key();
+            const TypeEntryCPtr &externalType = it.key();
             s << "// Extended implicit conversions for "
                 << externalType->qualifiedTargetLangName() << '.' << '\n';
             for (const auto &sourceClass : it.value()) {
@@ -6347,18 +6562,6 @@ bool CppGenerator::finishGeneration()
         s << '\n';
     }
 
-    // Implicit smart pointers conversions
-    const auto &smartPointersList = api().instantiatedSmartPointers();
-    if (!smartPointersList.isEmpty()) {
-        s << "// SmartPointers converters.\n\n";
-        for (const auto &smp : smartPointersList) {
-            s << "// C++ to Python conversion for smart pointer type '"
-                << smp.type.cppSignature() << "'.\n";
-            writeSmartPointerConverterFunctions(s, smp.type);
-        }
-        s << '\n';
-    }
-
     s << "static struct PyModuleDef moduledef = {\n"
         << "    /* m_base     */ PyModuleDef_HEAD_INIT,\n"
         << "    /* m_name     */ \"" << moduleName() << "\",\n"
@@ -6412,7 +6615,7 @@ bool CppGenerator::finishGeneration()
 
         collectFullTypeNamesArray(typeNames);
 
-        for (auto typeName : typeNames)
+        for (const auto &typeName : typeNames)
             s << "{nullptr, \"" << typeName << "\"},\n";
 
         s << "{nullptr, nullptr}\n" << outdent << "};\n"
@@ -6468,19 +6671,22 @@ bool CppGenerator::finishGeneration()
     if (!opaqueContainers.isEmpty()) {
         s << "\n// Opaque container type registration\n"
             << "PyObject *ob_type{};\n";
+        if (usePySideExtensions()) {
+            const bool hasQVariantConversion =
+                std::any_of(opaqueContainers.cbegin(), opaqueContainers.cend(),
+                            [](const OpaqueContainerData &d) { return d.hasQVariantConversion; });
+            if (hasQVariantConversion) {
+                const char qVariantConverterVar[] = "qVariantConverter";
+                s << "auto *" << qVariantConverterVar
+                  << " = Shiboken::Conversions::getConverter(\"QVariant\");\n"
+                  << "Q_ASSERT(" << qVariantConverterVar << " != nullptr);\n";
+            }
+        }
         for (const auto &d : opaqueContainers)
             s << d.registrationCode;
         s << '\n';
     }
 
-    if (!smartPointersList.isEmpty()) {
-        s << '\n';
-        for (const auto &smp : smartPointersList) {
-            writeSmartPointerConverterInitialization(s, smp.type);
-            s << '\n';
-        }
-    }
-
     if (!extendedConverters.isEmpty()) {
         s << '\n';
         for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
@@ -6499,7 +6705,9 @@ bool CppGenerator::finishGeneration()
         if (!pte->referencesType())
             continue;
         TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte);
-        registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType));
+        s << registerConverterName(pte->qualifiedCppName(), converterObject(referencedType),
+                                   registerConverterName::Alias
+                                   | registerConverterName::PartiallyQualifiedAliases);
     }
 
     s << '\n';
@@ -6542,8 +6750,12 @@ bool CppGenerator::finishGeneration()
     }
 
     // finish the rest of get_signature() initialization.
-    s << "FinishSignatureInitialization(module, " << moduleName()
-        << "_SignatureStrings);\n"
+    s << outdent << "#if PYSIDE6_COMOPT_COMPRESS == 0\n" << indent
+        << "FinishSignatureInitialization(module, " << moduleName() << "_SignatureStrings);\n"
+        << outdent << "#else\n" << indent
+        << "if (FinishSignatureInitBytes(module, " << moduleName() << "_SignatureBytes, "
+        << moduleName() << "_SignatureByteSize) < 0)\n" << indent << "return {};\n" << outdent
+        << outdent << "#endif\n" << indent
         << "\nreturn module;\n" << outdent << "}\n";
 
     file.done();
@@ -6683,9 +6895,10 @@ void CppGenerator::writeReturnValueHeuristics(TextStream &s, const AbstractMetaF
     }
 }
 
-void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context)
+void CppGenerator::writeHashFunction(TextStream &s, TextStream &t, const GeneratorContext &context)
 {
     const auto metaClass = context.metaClass();
+    t << fullPythonClassName(metaClass) << ".__hash__(self)->int\n";
     const char hashType[] = "Py_hash_t";
     s << "static " << hashType << ' ' << cpythonBaseName(metaClass)
         << "_HashFunc(PyObject *self)\n{\n" << indent;
@@ -6724,7 +6937,7 @@ void CppGenerator::writeDefaultSequenceMethods(TextStream &s,
     s << "PyObject *" << namePrefix
         << "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n" << indent;
     writeCppSelfDefinition(s, context, errorReturn);
-    writeIndexError(s, u"index out of bounds"_s, errorReturn);
+    writeIndexError(s, ErrorReturn::NullPtr);
 
     s << metaClass->qualifiedCppName() << "::const_iterator _item = "
         << CPP_SELF_VAR << "->begin();\n"
@@ -6750,7 +6963,7 @@ void CppGenerator::writeDefaultSequenceMethods(TextStream &s,
         << indent;
     errorReturn = ErrorReturn::MinusOne;
     writeCppSelfDefinition(s, context, errorReturn);
-    writeIndexError(s, u"list assignment index out of range"_s, errorReturn);
+    writeIndexError(s, errorReturn);
 
     s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n"
         << "if (!";
@@ -6768,11 +6981,11 @@ void CppGenerator::writeDefaultSequenceMethods(TextStream &s,
 
     s << "return {};\n" << outdent << "}\n";
 }
-void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg,
-                                   ErrorReturn errorReturn)
+void CppGenerator::writeIndexError(TextStream &s, ErrorReturn errorReturn)
 {
-    s << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n"
-        << indent << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n"
+    s << "const Py_ssize_t size = " << CPP_SELF_VAR << "->size();\n"
+        << "if (_i < 0 || _i >= size) {\n"
+        << indent << "Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);\n"
         << errorReturn << outdent << "}\n";
 }
 
index f369ab01bdaac7438318f363f6943993ddaccee3..dc4d4a96c97b56cbcf29c2deaf53998ccce3269f 100644 (file)
@@ -29,6 +29,7 @@ public:
         Default, // "{}"
         Zero,
         MinusOne,
+        NullPtr,
         Void
     };
 
@@ -45,7 +46,11 @@ public:
 
 protected:
     QString fileNameForContext(const GeneratorContext &context) const override;
-    void generateClass(TextStream &s, const GeneratorContext &classContext) override;
+    void generateClass(TextStream &s, const QString &targetDir,
+                       const GeneratorContext &classContext,
+                       QList<GeneratorContext> *contexts) override;
+    void generateSmartPointerClass(TextStream &s, const QString &targetDir,
+                                   const GeneratorContext &classContext) override;
     bool finishGeneration() override;
 
 private:
@@ -55,15 +60,13 @@ private:
         bool needsReference = false;
     };
 
-
-    void generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext);
     void generateIncludes(TextStream &s, const GeneratorContext &classContext,
                           const IncludeGroupList &includes = {},
                           const AbstractMetaClassCList &innerClasses = {}) const;
-    static void writeInitFunc(TextStream &declStr, TextStream &callStr,
-                              const QString &initFunctionName,
-                              const TypeEntryCPtr &enclosingEntry,
-                              const QString &pythonName, bool lazy = true);
+    static void writeInitFuncCall(TextStream &callStr,
+                                  const QString &functionName,
+                                  const TypeEntryCPtr &enclosingEntry,
+                                  const QString &pythonName, bool lazy = true);
     static void writeCacheResetNative(TextStream &s, const GeneratorContext &classContext);
     void writeConstructorNative(TextStream &s, const GeneratorContext &classContext,
                                 const AbstractMetaFunctionCPtr &func) const;
@@ -121,6 +124,7 @@ private:
         QString converterCheckFunctionName;
         QString pythonToConverterFunctionName;
         QString registrationCode;
+        bool hasQVariantConversion = false;
     };
 
     OpaqueContainerData
@@ -148,6 +152,7 @@ private:
                             const AbstractMetaFunctionCList &overloads,
                             const GeneratorContext &classContext) const;
     static void writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData,
+                                          const GeneratorContext &classContext,
                                           ErrorReturn errorReturn = ErrorReturn::Default);
     static void writeCppSelfConversion(TextStream &s,
                                        const GeneratorContext &context,
@@ -176,6 +181,7 @@ private:
                                   ErrorReturn errorReturn);
 
     static QString returnErrorWrongArguments(const OverloadData &overloadData,
+                                             const GeneratorContext &context,
                                              ErrorReturn errorReturn);
 
     static void writeFunctionReturnErrorCheckSection(TextStream &s,
@@ -285,6 +291,7 @@ private:
      *   \param overloadData the overload data describing all the possible overloads for the function/method
      */
     void writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData,
+                                        const GeneratorContext &classContext,
                                         ErrorReturn errorReturn) const;
     /// Recursive auxiliar method to the other writeOverloadedFunctionDecisor.
     void writeOverloadedFunctionDecisorEngine(TextStream &s,
@@ -371,6 +378,7 @@ private:
                                              const AbstractMetaFunctionCPtr &func,
                                              bool usePyArgs,
                                              const OverloadData &overloadData,
+                                             const GeneratorContext &classContext,
                                              ErrorReturn errorReturn);
 
     /// Returns a string containing the name of an argument for the given function and argument index.
@@ -411,8 +419,9 @@ private:
     QByteArrayList methodDefinitionParameters(const OverloadData &overloadData) const;
     QList<PyMethodDefEntry> methodDefinitionEntries(const OverloadData &overloadData) const;
 
-    void writeSignatureInfo(TextStream &s, const OverloadData &overloads) const;
-    QString signatureParameter(const AbstractMetaArgument &arg) const;
+    void writeSignatureInfo(TextStream &s, const OverloadData &overloads,
+                            bool useProperties=false) const;
+    QString signatureParameter(const AbstractMetaArgument &arg, bool implicitConversions) const;
     QString pythonSignature(const AbstractMetaType &type) const;
     /// Writes the implementation of all methods part of python sequence protocol
     void writeSequenceMethods(TextStream &s,
@@ -459,7 +468,7 @@ private:
     static void writeRichCompareFunctionHeader(TextStream &s,
                                                const QString &baseName,
                                                const GeneratorContext &context);
-    void writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const;
+    void writeRichCompareFunction(TextStream &s, TextStream &t, const GeneratorContext &context) const;
     void writeSmartPointerRichCompareFunction(TextStream &s, const GeneratorContext &context) const;
 
     static void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums);
@@ -484,6 +493,7 @@ private:
     void writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &ype) const;
 
     static QString typeInitStruct(const TypeEntryCPtr &te);
+    static QString typeInitStruct(const GeneratorContext &context);
     static void writeExtendedConverterInitialization(TextStream &s,
                                                      const TypeEntryCPtr &externalType,
                                                      const AbstractMetaClassCList &conversions);
@@ -522,13 +532,12 @@ private:
     static bool shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass);
 
     static bool hasHashFunction(const AbstractMetaClassCPtr &c);
-    static void writeHashFunction(TextStream &s, const GeneratorContext &context);
+    static void writeHashFunction(TextStream &s, TextStream &t, const GeneratorContext &context);
 
     /// Write default implementations for sequence protocol
     void writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const;
     /// Helper function for writeStdListWrapperMethods.
-    static void writeIndexError(TextStream &s, const QString &errorMsg,
-                                ErrorReturn errorReturn);
+    static void writeIndexError(TextStream &s, ErrorReturn errorReturn);
 
     static QString writeReprFunctionHeader(TextStream &s, const GeneratorContext &context);
     static QString writeReprFunction(TextStream &s,
@@ -554,6 +563,8 @@ private:
     void clearTpFuncs();
     static QString chopType(QString s);
 
+    static QString typeInitStructHelper(const TypeEntryCPtr &te, const QString &varName);
+
     QHash<QString, QString> m_tpFuncs;
     QHash<QString, QString> m_nbFuncs;
 };
index 00e0cabeac918a98e68f8872056d2bc2390a2dbc..cffea253284c177027215973a83d550affacac98 100644 (file)
@@ -96,6 +96,31 @@ static void writeContainerCreationFunc(TextStream &s,
         << "}\n\n";
 }
 
+static void writeConverterCheckFunc(TextStream &s,
+                                    const QString &name,
+                                    const QString &checkFunction,
+                                    const QString &converterFunction)
+{
+    s << "extern \"C\" PythonToCppFunc " << name << "(PyObject *" << PYTHON_ARG << ")\n{\n"
+      << indent << "if (" << checkFunction << '(' << PYTHON_ARG << "))\n" << indent
+      << "return " << converterFunction << ";\n"
+      << outdent << "return {};\n" << outdent << "}\n\n";
+}
+
+static void writeConverterFunctionHeader(TextStream &s,
+                                         const QString &name,
+                                         const QString &typeName)
+{
+    s << "extern \"C\" void " << name << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n"
+      << indent << "auto *d = ShibokenSequenceContainerPrivate<" << typeName
+      << ">::get(" << PYTHON_ARG << ");\n";
+}
+
+static inline void writeConverterFunctionFooter(TextStream &s)
+{
+    s << outdent << "}\n\n";
+}
+
 // Generate template specialization of value converter helper
 void CppGenerator::writeOpaqueContainerValueConverter(TextStream &s,
                                                       const AbstractMetaType &valueType) const
@@ -166,6 +191,7 @@ CppGenerator::OpaqueContainerData
     // methods
     const QString &containerName = containerType.name();
     const bool isStdVector = containerName  == u"std::vector";
+    result.hasQVariantConversion = usePySideExtensions() && containerName == "QList"_L1;
     const auto kind = containerTypeEntry->containerKind();
     const bool isFixed = kind == ContainerTypeEntry::SpanContainer || containerName == u"std::array";
     const QString methods = result.name + u"_methods"_s;
@@ -216,8 +242,8 @@ CppGenerator::OpaqueContainerData
     // type creation function that sets a key in the type dict.
     const QString typeCreationFName =  u"create"_s + result.name + u"Type"_s;
     s << "static inline PyTypeObject *" << typeCreationFName << "()\n{\n" << indent
-        << "auto *result = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&"
-        << specName <<  "));\nPy_INCREF(Py_True);\n"
+        << "auto *result = SbkType_FromSpec(&"
+        << specName <<  ");\nPy_INCREF(Py_True);\n"
         << "Shiboken::AutoDecRef tpDict(PepType_GetDict(result));\n"
         << "PyDict_SetItem(tpDict.object(), "
            "Shiboken::PyMagicName::opaque_container(), Py_True);\n"
@@ -249,24 +275,39 @@ CppGenerator::OpaqueContainerData
 
     // SBK converter Python to C++
     result.pythonToConverterFunctionName = u"PythonToCpp"_s + result.name;
-    s << "extern \"C\" void " << result.pythonToConverterFunctionName
-        << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n" << indent
-        << "auto *d = ShibokenSequenceContainerPrivate<" << cppSignature
-        << ">::get(" << PYTHON_ARG << ");\n"
-        << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n"
-        << outdent << "}\n\n";
+    writeConverterFunctionHeader(s, result.pythonToConverterFunctionName, cppSignature);
+    s << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n";
+    writeConverterFunctionFooter(s);
 
     // SBK check function for converting Python to C++ that returns the converter
     result.converterCheckFunctionName = u"is"_s + result.name + u"PythonToCppConvertible"_s;
-    s << "extern \"C\" PythonToCppFunc " << result.converterCheckFunctionName
-        << "(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "if ("
-        << result.checkFunctionName << '(' << PYTHON_ARG << "))\n" << indent
-        << "return " << result.pythonToConverterFunctionName << ";\n"
-        << outdent << "return {};\n" << outdent << "}\n\n";
-
-    QTextStream(&result.registrationCode) << "ob_type = reinterpret_cast<PyObject *>("
-        << typeFName
-        << "());\nPy_XINCREF(ob_type);\nPyModule_AddObject(module, \""
+    writeConverterCheckFunc(s, result.converterCheckFunctionName, result.checkFunctionName,
+                            result.pythonToConverterFunctionName);
+
+    TextStream registrationStr(&result.registrationCode, TextStream::Language::Cpp);
+    registrationStr << "ob_type = reinterpret_cast<PyObject *>("
+        << typeFName << "());\nPy_XINCREF(ob_type);\nPyModule_AddObject(module, \""
         << result.name << "\", ob_type);\n";
+
+    if (!result.hasQVariantConversion)
+        return result;
+
+    // PYSIDE-2820: Add a conversion to QVariant for QML. Note QVariant::fromValue()
+    // will work for all list types, but we restrict it to registered meta types for QML.
+    QString pythonToQVariantConverterFunctionName = "PythonToQVariant"_L1 + result.name;
+    QString pythonToQVariantCheckFunctionName = "is"_L1 + result.name +
+                                                "PythonToQVariantConvertible"_L1;
+    writeConverterFunctionHeader(s, pythonToQVariantConverterFunctionName, cppSignature);
+    s << "*reinterpret_cast<QVariant *>(cppOut) = QVariant::fromValue(*d->m_list);\n";
+    writeConverterFunctionFooter(s);
+    writeConverterCheckFunc(s, pythonToQVariantCheckFunctionName,
+                            result.checkFunctionName,
+                            pythonToQVariantConverterFunctionName);
+    registrationStr << "if constexpr (QMetaTypeId2<" << valueType.cppSignature()
+        << ">::Defined) {\n" << indent
+        << "Shiboken::Conversions::prependPythonToCppValueConversion(qVariantConverter,\n    "
+        << pythonToQVariantConverterFunctionName << ", "
+        << pythonToQVariantCheckFunctionName << ");\n" << outdent << "}\n";
+
     return result;
 }
index a00c2ba50b2cf8e7991fd2ab45a31a41acd47cd4..6245b24a841ffa80339dbb7646bbbe6ca6d4e939 100644 (file)
@@ -91,7 +91,9 @@ static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f)
     return !f->arguments().isEmpty();
 }
 
-void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext)
+void CppGenerator::generateSmartPointerClass(TextStream &s,
+                                             const QString & /* targetDir */,
+                                             const GeneratorContext &classContext)
 {
     s.setLanguage(TextStream::Language::Cpp);
     AbstractMetaClassCPtr metaClass = classContext.metaClass();
@@ -177,8 +179,8 @@ void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorConte
     writeCopyFunction(s, md, signatureStream, classContext);
     writeSmartPointerDirFunction(s, md, signatureStream, classContext);
 
-    const QString methodsDefinitions = md.toString();
-    const QString singleMethodDefinitions = smd.toString();
+    const QString &methodsDefinitions = md.toString();
+    const QString &singleMethodDefinitions = smd.toString();
 
     const QString className = chopType(cpythonTypeName(typeEntry));
 
@@ -202,7 +204,7 @@ void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorConte
     s << closeExternC;
 
     if (hasHashFunction(metaClass))
-        writeHashFunction(s, classContext);
+        writeHashFunction(s, signatureStream, classContext);
 
     // Write tp_traverse and tp_clear functions.
     writeTpTraverseFunction(s, metaClass);
@@ -213,6 +215,8 @@ void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorConte
     s << '\n';
 
     writeConverterFunctions(s, metaClass, classContext);
+    // Implicit smart pointers conversions
+    writeSmartPointerConverterFunctions(s, classContext.preciseType());
     writeClassRegister(s, metaClass, classContext, signatureStream);
 
     // class inject-code native/end
index 35d0d114f8b488c77eaef2db2b9cd6ec226a637f..bf5ad29d03f061f46778b2c977d89bc1e4e5a6d4 100644 (file)
@@ -109,7 +109,21 @@ static void writeProtectedEnums(TextStream &s, const AbstractMetaClassCPtr &meta
     }
 }
 
-void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+void HeaderGenerator::generateClass(TextStream &s, const QString &,
+                                    const GeneratorContext &classContext,
+                                    QList<GeneratorContext> *)
+{
+    doGenerateClass(s, classContext);
+}
+
+void HeaderGenerator::generateSmartPointerClass(TextStream &s,
+                                                const QString &,
+                                                const GeneratorContext &classContext)
+{
+    doGenerateClass(s, classContext);
+}
+
+void HeaderGenerator::doGenerateClass(TextStream &s, const GeneratorContext &classContext) const
 {
     const AbstractMetaClassCPtr metaClass = classContext.metaClass();
 
@@ -128,7 +142,7 @@ void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &class
 
     // Includes
     s << metaClass->typeEntry()->include() << '\n';
-    for (auto &inst : metaClass->templateBaseClassInstantiations())
+    for (const auto &inst : metaClass->templateBaseClassInstantiations())
         s << inst.typeEntry()->include();
 
     if (classContext.useWrapper())
@@ -198,9 +212,6 @@ void HeaderGenerator::writeWrapperClassDeclaration(TextStream &s,
     const auto typeEntry = metaClass->typeEntry();
     InheritedOverloadSet inheritedOverloads;
 
-    // write license comment
-    s << licenseComment();
-
     // Class
     s << "class " << wrapperName
       << " : public " << metaClass->qualifiedCppName()
@@ -236,8 +247,6 @@ void HeaderGenerator::writeWrapperClassDeclaration(TextStream &s,
         if (generation.testFlag(FunctionGenerationFlag::VirtualMethod))
             maxOverrides++;
     }
-    if (!maxOverrides)
-        maxOverrides = 1;
 
     //destructor
     // PYSIDE-504: When C++ 11 is used, then the destructor must always be declared.
@@ -273,8 +282,13 @@ void *qt_metacast(const char *_clname) override;
     if (usePySideExtensions())
         s << "static void pysideInitQtMetaTypes();\n";
 
-    s << "void resetPyMethodCache();\n"
-        << outdent << "private:\n" << indent;
+    const bool needsMethodCache = useOverrideCaching(metaClass);
+    Q_ASSERT(maxOverrides > 0 || !needsMethodCache);
+
+    if (needsMethodCache)
+        s << "void resetPyMethodCache();\n";
+
+    s << outdent << "private:\n" << indent;
 
     if (!metaClass->userAddedPythonOverrides().isEmpty()) {
         for (const auto &f : metaClass->userAddedPythonOverrides())
@@ -282,8 +296,14 @@ void *qt_metacast(const char *_clname) override;
         s << '\n';
     }
 
-    s << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"
-      << outdent << "};\n\n";
+    if (needsMethodCache) {
+        s << "mutable bool m_PyMethodCache[" << maxOverrides << "] = {false";
+        for (int i = 1; i < maxOverrides; ++i)
+            s << ", false";
+        s << "};\n";
+    }
+
+    s << outdent << "};\n\n";
 }
 
 // Write an inline wrapper around a function
@@ -292,7 +312,6 @@ void HeaderGenerator::writeMemberFunctionWrapper(TextStream &s,
                                                  const QString &postfix) const
 {
     Q_ASSERT(!func->isConstructor() && !func->isOperatorOverload());
-    s << "inline ";
     s << functionSignature(func, {}, postfix, Generator::OriginalTypeDescription)
       << " { ";
     if (!func->isVoid())
@@ -526,7 +545,7 @@ static qsizetype indexOf(const NameSpaces &nsps, const AbstractMetaClassCPtr &ne
 static void writeNamespaceForwardDeclarationRecursion(TextStream &s, qsizetype idx,
                                                       const NameSpaces &nameSpaces)
 {
-    auto &root = nameSpaces.at(idx);
+    const auto &root = nameSpaces.at(idx);
     s << '\n';
     if (root.nameSpace->isInlineNamespace())
         s << "inline ";
@@ -915,8 +934,10 @@ void HeaderGenerator::writeTypeFunctions(TextStream &s, const QString &typeFunct
 
 void HeaderGenerator::writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum)
 {
-    if (avoidProtectedHack() && cppEnum.isProtected())
-        s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};\n";
+    if (avoidProtectedHack() && cppEnum.isProtected()) {
+        s << "enum " << protectedEnumSurrogateName(cppEnum) << " : " <<
+          cppEnum.underlyingType() << " {};\n";
+    }
 }
 
 void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum)
index 03b98e7438a097224603d39a01f71de9a9386cde..814d012f124c3950f27dffcce0e6265db78562c7 100644 (file)
@@ -27,13 +27,19 @@ public:
 
 protected:
     QString fileNameForContext(const GeneratorContext &context) const override;
-    void generateClass(TextStream &s, const GeneratorContext &classContext) override;
+    void generateClass(TextStream &s, const QString &targetDir,
+                       const GeneratorContext &classContext,
+                       QList<GeneratorContext> *contexts) override;
+    void generateSmartPointerClass(TextStream &s,
+                                   const QString &targetDir,
+                                   const GeneratorContext &classContext) override;
     bool finishGeneration() override;
 
 private:
     using InheritedOverloadSet = QSet<AbstractMetaFunctionCPtr>;
     using IndexValues = QList<IndexValue>;
 
+    void doGenerateClass(TextStream &s, const GeneratorContext &classContext) const;
     IndexValues collectTypeIndexes(const AbstractMetaClassCList &classList);
     IndexValues collectConverterIndexes() const;
 
@@ -73,4 +79,3 @@ private:
 };
 
 #endif // HEADERGENERATOR_H
-
index c28fcdc1a196f967cf262cee06bb82c3ca40c245..a9e652c6d70b2f3a6688b6c8e50a5d49f770cd45 100644 (file)
@@ -453,7 +453,7 @@ OverloadDataNode::OverloadDataNode(const AbstractMetaFunctionCPtr &func,
                                    OverloadDataRootNode *parent,
                                    const AbstractMetaArgument &argument,
                                    int argPos,
-                                   const QString argTypeReplaced) :
+                                   const QString &argTypeReplaced) :
       m_argument(argument),
       m_argTypeReplaced(argTypeReplaced),
       m_parent(parent),
@@ -494,82 +494,69 @@ OverloadDataNode *OverloadDataRootNode::addOverloadDataNode(const AbstractMetaFu
     return overloadData.get();
 }
 
+static bool isNonVoidOverload(const AbstractMetaFunctionCPtr &func)
+{
+    return func->isTypeModified()
+        ? func->modifiedTypeName() != "void"_L1
+        : !func->argumentRemoved(0) && !func->type().isVoid();
+}
+
 bool OverloadData::hasNonVoidReturnType() const
 {
-    for (const auto &func : m_overloads) {
-        if (func->isTypeModified()) {
-            if (func->modifiedTypeName() != u"void")
-                return true;
-        } else {
-            if (!func->argumentRemoved(0) && !func->type().isVoid())
-                return true;
-        }
-    }
-    return false;
+    return std::any_of(m_overloads.cbegin(), m_overloads.cend(),
+                       isNonVoidOverload);
+}
+
+static bool functionHasVarargs(const AbstractMetaFunctionCPtr &func)
+{
+    const auto &args = func->arguments();
+    return args.size() > 1 && args.constLast().type().isVarargs();
 }
 
 bool OverloadData::hasVarargs() const
 {
-    for (const auto &func : m_overloads) {
-        AbstractMetaArgumentList args = func->arguments();
-        if (args.size() > 1 && args.constLast().type().isVarargs())
-            return true;
-    }
-    return false;
+    return std::any_of(m_overloads.cbegin(), m_overloads.cend(),
+                       functionHasVarargs);
+}
+
+static bool isStaticFunction(const AbstractMetaFunctionCPtr &func)
+{
+    return func->isStatic();
 }
 
 bool OverloadData::hasStaticFunction(const AbstractMetaFunctionCList &overloads)
 {
-    for (const auto &func : overloads) {
-        if (func->isStatic())
-            return true;
-    }
-    return false;
+    return std::any_of(overloads.cbegin(), overloads.cend(), isStaticFunction);
 }
 
 bool OverloadData::hasStaticFunction() const
 {
-    for (const auto &func : m_overloads) {
-        if (func->isStatic())
-            return true;
-    }
-    return false;
+    return std::any_of(m_overloads.cbegin(), m_overloads.cend(), isStaticFunction);
+}
+
+static bool isClassMethod(const AbstractMetaFunctionCPtr &func)
+{
+    return func->isClassMethod();
 }
 
 bool OverloadData::hasClassMethod(const AbstractMetaFunctionCList &overloads)
 {
-    for (const auto &func : overloads) {
-        if (func->isClassMethod())
-            return true;
-    }
-    return false;
+    return std::any_of(overloads.cbegin(), overloads.cend(), isClassMethod);
 }
 
 bool OverloadData::hasClassMethod() const
 {
-    for (const auto &func : m_overloads) {
-        if (func->isClassMethod())
-            return true;
-    }
-    return false;
+    return std::any_of(m_overloads.cbegin(), m_overloads.cend(), isClassMethod);
 }
 
 bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionCList &overloads)
 {
-    for (const auto &func : overloads) {
-        if (!func->isStatic())
-            return true;
-    }
-    return false;
+    return !std::all_of(overloads.cbegin(), overloads.cend(), isStaticFunction);
 }
 
 bool OverloadData::hasInstanceFunction() const
 {
-    for (const auto &func : m_overloads) {
-        if (!func->isStatic())
-            return true;
-    }
-    return false;
+    return !std::all_of(m_overloads.cbegin(), m_overloads.cend(), isStaticFunction);
 }
 
 bool OverloadData::hasStaticAndInstanceFunctions(const AbstractMetaFunctionCList &overloads)
@@ -611,13 +598,14 @@ const AbstractMetaArgument *OverloadDataNode::overloadArgument(const AbstractMet
     return &func->arguments().at(m_argPos + removed);
 }
 
+static bool hasDefaultValue(const OverloadDataNodePtr &o)
+{
+    return o->getFunctionWithDefaultValue() != nullptr;
+}
+
 bool OverloadDataRootNode::nextArgumentHasDefaultValue() const
 {
-    for (const auto &overloadData : m_children) {
-        if (overloadData->getFunctionWithDefaultValue())
-            return true;
-    }
-    return false;
+     return std::any_of(m_children.cbegin(), m_children.cend(), hasDefaultValue);
 }
 
 static const OverloadDataRootNode *_findNextArgWithDefault(const OverloadDataRootNode *overloadData)
@@ -642,11 +630,10 @@ const OverloadDataRootNode *OverloadDataRootNode::findNextArgWithDefault() const
 
 bool OverloadDataRootNode::isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const
 {
-    for (const auto &pd : m_children) {
-        if (pd->overloads().contains(func))
-            return false;
-    }
-    return true;
+    auto containsPredicate = [&func](const OverloadDataNodePtr &o) {
+        return o->overloads().contains(func);
+    };
+    return std::none_of(m_children.cbegin(), m_children.cend(), containsPredicate);
 }
 
 AbstractMetaFunctionCPtr OverloadDataRootNode::getFunctionWithDefaultValue() const
@@ -893,23 +880,20 @@ bool OverloadData::pythonFunctionWrapperUsesListOfArguments() const
 
 bool OverloadData::hasArgumentWithDefaultValue() const
 {
-    if (maxArgs() == 0)
-        return false;
-    for (const auto &func : m_overloads) {
-        if (hasArgumentWithDefaultValue(func))
-            return true;
-    }
-    return false;
+    return maxArgs() != 0
+           && std::any_of(m_overloads.cbegin(), m_overloads.cend(),
+                          qOverload<const AbstractMetaFunctionCPtr &>(hasArgumentWithDefaultValue));
+}
+
+static bool isArgumentWithDefaultValue(const AbstractMetaArgument &arg)
+{
+    return !arg.isModifiedRemoved() && arg.hasDefaultValueExpression();
 }
 
 bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionCPtr &func)
 {
     const AbstractMetaArgumentList &arguments = func->arguments();
-    for (const AbstractMetaArgument &arg : arguments) {
-        if (!arg.isModifiedRemoved() && arg.hasDefaultValueExpression())
-            return true;
-    }
-    return false;
+    return std::any_of(arguments.cbegin(), arguments.cend(), isArgumentWithDefaultValue);
 }
 
 AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunctionCPtr &func)
index 875a5a8b50d88ead1c4b97092bd149a6f9cf40c9..48d83cdb7bcca61820842af5ddea0ac981b379a6 100644 (file)
@@ -82,7 +82,7 @@ public:
     explicit OverloadDataNode(const AbstractMetaFunctionCPtr &func,
                               OverloadDataRootNode *parent,
                               const AbstractMetaArgument &arg, int argPos,
-                              const QString argTypeReplaced = {});
+                              const QString &argTypeReplaced = {});
     void addOverload(const AbstractMetaFunctionCPtr &func);
 
     int argPos() const override { return m_argPos; }
index a1417e5d9e360cf4236ca588152db87dc985205d..68ee818005aa88810b36647e4ad7c78b32ac511b 100644 (file)
@@ -107,7 +107,7 @@ struct GeneratorClassInfoCacheEntry
     ShibokenGenerator::FunctionGroups functionGroups;
     QList<AbstractMetaFunctionCList> numberProtocolOperators;
     BoolCastFunctionOptional boolCastFunctionO;
-    bool needsGetattroFunction = false;
+    ShibokenGenerator::AttroCheck attroCheck;
 };
 
 using GeneratorClassInfoCache = QHash<AbstractMetaClassCPtr, GeneratorClassInfoCacheEntry>;
@@ -412,28 +412,28 @@ QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum &me
     return result + u"_Surrogate"_s;
 }
 
+QString ShibokenGenerator::cpythonConstructorName(const AbstractMetaClassCPtr &metaClass)
+{
+    return cpythonBaseName(metaClass->typeEntry()) + u"_Init"_s;
+}
+
 QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &func)
 {
-    QString result;
+    if (func->isConstructor())
+        return cpythonConstructorName(func->implementingClass());
 
+    QString result;
     // PYSIDE-331: For inherited functions, we need to find the same labels.
     // Therefore we use the implementing class.
     if (func->implementingClass()) {
-        result = cpythonBaseName(func->implementingClass()->typeEntry());
-        if (func->isConstructor()) {
-            result += u"_Init"_s;
-        } else {
-            result += u"Func_"_s;
-            if (func->isOperatorOverload())
-                result += ShibokenGenerator::pythonOperatorFunctionName(func);
-            else
-                result += func->name();
-        }
-    } else {
-        result = u"Sbk"_s + moduleName() + u"Module_"_s + func->name();
+        return cpythonBaseName(func->implementingClass()->typeEntry())
+            + u"Func_"_s
+            + (func->isOperatorOverload()
+               ? ShibokenGenerator::pythonOperatorFunctionName(func)
+               : func->name());
     }
 
-    return result;
+    return u"Sbk"_s + moduleName() + u"Module_"_s + func->name();
 }
 
 QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func)
@@ -552,7 +552,7 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntryCPtr &type,
                                               const QString &argName)
 {
     if (!type->isWrapperType())
-        return QString();
+        return {};
     return u"reinterpret_cast< "_s + getFullTypeName(type)
         + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(type)
         + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s;
@@ -688,27 +688,39 @@ QString ShibokenGenerator::converterObject(const AbstractMetaType &type)
     return converterObject(typeEntry);
 }
 
+static QString sbkEnumPrivate(const QString &name)
+{
+    return "PepType_SETP(reinterpret_cast<SbkEnumType *>("_L1 + name  + "))"_L1;
+}
+
 QString ShibokenGenerator::converterObject(const TypeEntryCPtr &type)
 {
-    if (isExtendedCppPrimitive(type))
-        return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()")
-                                  .arg(type->qualifiedCppName());
-    if (type->isWrapperType())
-        return QString::fromLatin1("PepType_SOTP(reinterpret_cast<PyTypeObject *>(%1))->converter")
-                                  .arg(cpythonTypeNameExt(type));
-    if (type->isEnum() || type->isFlags())
-        return QString::fromLatin1("PepType_SETP(reinterpret_cast<SbkEnumType *>(%1))->converter")
-                                  .arg(cpythonTypeNameExt(type));
+    if (isExtendedCppPrimitive(type)) {
+        return "Shiboken::Conversions::PrimitiveTypeConverter<"_L1
+               + type->qualifiedCppName() + ">()"_L1;
+    }
+
+    if (type->isWrapperType()) {
+        return "PepType_SOTP(reinterpret_cast<PyTypeObject *>("_L1
+               + cpythonTypeNameExt(type) + "))->converter"_L1;
+    }
+
+    if (type->isEnum())
+        return sbkEnumPrivate(cpythonTypeNameExt(type)) + "->converter"_L1;
+    if (type->isFlags())
+        return sbkEnumPrivate(cpythonTypeNameExt(type)) + "->flagsConverter"_L1;
 
     if (type->isArray()) {
-        qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName();
+        qCWarning(lcShiboken, "Warning: no idea how to handle the Qt type \"%s\"",
+                  qPrintable(type->qualifiedCppName()));
         return {};
     }
 
     /* the typedef'd primitive types case */
     auto pte = std::dynamic_pointer_cast<const PrimitiveTypeEntry>(type);
     if (!pte) {
-        qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName();
+        qCWarning(lcShiboken, "Warning: the Qt primitive type \"%s\" is unknown",
+                  qPrintable(type->qualifiedCppName()));
         return {};
     }
     pte = basicReferencedTypeEntry(pte);
@@ -874,7 +886,7 @@ bool ShibokenGenerator::isNullPtr(const QString &value)
         || value == u"NULLPTR" || value == u"{}";
 }
 
-QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType)
+QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType &metaType)
 {
     const auto typeEntry = metaType.typeEntry();
     if (typeEntry->isCustom()) {
@@ -1292,13 +1304,13 @@ static QString getArgumentsFromMethodCall(const QString &str)
     static QLatin1String funcCall("%CPPSELF.%FUNCTION_NAME");
     auto pos = str.indexOf(funcCall);
     if (pos == -1)
-        return QString();
+        return {};
     pos = pos + funcCall.size();
     while (str.at(pos) == u' ' || str.at(pos) == u'\t')
         ++pos;
     if (str.at(pos) == u'(')
         ++pos;
-    int begin = pos;
+    qsizetype begin = pos;
     int counter = 1;
     while (counter != 0) {
         if (str.at(pos) == u'(')
@@ -1493,9 +1505,9 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s,
         // Python argument on the binding virtual method.
         static const QRegularExpression pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"_L1);
         Q_ASSERT(pyArgsAttributionRegex.isValid());
-        code.replace(pyArgsAttributionRegex, u"PyTuple_SET_ITEM("_s
+        code.replace(pyArgsAttributionRegex, u"PyTuple_SetItem("_s
                      + PYTHON_ARGS + u".object(), \\1-1, \\2)"_s);
-        code.replace(pyArgsRegex, u"PyTuple_GET_ITEM("_s
+        code.replace(pyArgsRegex, u"PyTuple_GetItem("_s
                      + PYTHON_ARGS + u".object(), \\1-1)"_s);
     }
 
@@ -1753,13 +1765,13 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa
                                               typeSystemConvName().value(converterVariable),
                                               message));
         }
-        const auto conversionType = conversionTypeO.value();
+        const auto &conversionType = conversionTypeO.value();
         QString conversion;
         switch (converterVariable) {
             case TypeSystemToCppFunction: {
                 StringStream c(TextStream::Language::Cpp);
-                int end = match.capturedStart();
-                int start = end;
+                const auto end = match.capturedStart();
+                auto start = end;
                 while (start > 0 && code.at(start) != u'\n')
                     --start;
                 while (code.at(start).isSpace())
@@ -1845,48 +1857,56 @@ bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &con
 
 bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClassCPtr &metaClass)
 {
-    return metaClass->isPolymorphic();
+    return checkAttroFunctionNeeds(metaClass).testFlag(AttroCheckFlag::SetattroMethodOverride);
 }
 
-ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(
-    const AbstractMetaClassCPtr &metaClass)
+bool ShibokenGenerator::isVirtualOverride(const AbstractMetaFunctionCPtr &f)
+{
+    return f->isVirtual() && !f->isDestructor() &&
+        ShibokenGenerator::functionGeneration(f).testFlag(FunctionGenerationFlag::VirtualMethod);
+}
+
+ShibokenGenerator::AttroCheck
+    ShibokenGenerator::checkAttroFunctionNeedsImpl(const AbstractMetaClassCPtr &metaClass,
+                                                   const FunctionGroups &functionGroups)
 {
-    AttroCheck result;
     if (metaClass->typeEntry()->isSmartPointer()) {
-        result |= AttroCheckFlag::GetattroSmartPointer | AttroCheckFlag::SetattroSmartPointer;
-    } else {
-        if (getGeneratorClassInfo(metaClass).needsGetattroFunction)
-            result |= AttroCheckFlag::GetattroOverloads;
-        if (metaClass->queryFirstFunction(metaClass->functions(),
-                                          FunctionQueryOption::GetAttroFunction)) {
-            result |= AttroCheckFlag::GetattroUser;
-        }
-        if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT)
-            result |= AttroCheckFlag::SetattroQObject;
-        if (useOverrideCaching(metaClass))
+        return AttroCheck(AttroCheckFlag::GetattroSmartPointer
+                          | AttroCheckFlag::SetattroSmartPointer);
+    }
+
+    AttroCheck result;
+    if (classNeedsGetattroOverloadFunctionImpl(functionGroups))
+        result |= AttroCheckFlag::GetattroOverloads;
+    if (metaClass->queryFirstFunction(metaClass->functions(),
+                                      FunctionQueryOption::GetAttroFunction)) {
+        result |= AttroCheckFlag::GetattroUser;
+    }
+    if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT)
+        result |= AttroCheckFlag::SetattroQObject;
+    if (metaClass->isPolymorphic()) {
+        const auto &funcs = metaClass->functions();
+        if (std::any_of(funcs.cbegin(), funcs.cend(), isVirtualOverride))
             result |= AttroCheckFlag::SetattroMethodOverride;
-        if (metaClass->queryFirstFunction(metaClass->functions(),
-                                          FunctionQueryOption::SetAttroFunction)) {
-            result |= AttroCheckFlag::SetattroUser;
-        }
-        // PYSIDE-1255: If setattro is generated for a class inheriting
-        // QObject, the property code needs to be generated, too.
-        if ((result & AttroCheckFlag::SetattroMask) != 0
-            && !result.testFlag(AttroCheckFlag::SetattroQObject)
-            && isQObject(metaClass)) {
-            result |= AttroCheckFlag::SetattroQObject;
-        }
+    }
+    if (metaClass->queryFirstFunction(metaClass->functions(),
+                                      FunctionQueryOption::SetAttroFunction)) {
+        result |= AttroCheckFlag::SetattroUser;
+    }
+    // PYSIDE-1255: If setattro is generated for a class inheriting
+    // QObject, the property code needs to be generated, too.
+    if ((result & AttroCheckFlag::SetattroMask) != 0
+        && !result.testFlag(AttroCheckFlag::SetattroQObject)
+        && isQObject(metaClass)) {
+        result |= AttroCheckFlag::SetattroQObject;
     }
     return result;
 }
 
-bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass)
+// Returns whether a mixture of static and instance functions exists,
+// requiring a getattro function.
+bool ShibokenGenerator::classNeedsGetattroOverloadFunctionImpl(const FunctionGroups &functionGroup)
 {
-    if (!metaClass)
-        return false;
-    if (metaClass->typeEntry()->isSmartPointer())
-        return true;
-    const auto &functionGroup = getFunctionGroups(metaClass);
     for (auto it = functionGroup.cbegin(), end = functionGroup.cend(); it != end; ++it) {
         AbstractMetaFunctionCList overloads;
         for (const auto &func : std::as_const(it.value())) {
@@ -2035,13 +2055,13 @@ ShibokenGenerator::FunctionGroups ShibokenGenerator::getGlobalFunctionGroups() c
 const GeneratorClassInfoCacheEntry &
     ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClassCPtr &scope)
 {
-    auto cache = generatorClassInfoCache();
+    auto *cache = generatorClassInfoCache();
     auto it = cache->find(scope);
     if (it == cache->end()) {
         it = cache->insert(scope, {});
         auto &entry = it.value();
         entry.functionGroups = getFunctionGroupsImpl(scope);
-        entry.needsGetattroFunction = classNeedsGetattroFunctionImpl(scope);
+        entry.attroCheck = checkAttroFunctionNeedsImpl(scope, entry.functionGroups);
         entry.numberProtocolOperators = getNumberProtocolOperators(scope);
         entry.boolCastFunctionO = getBoolCast(scope);
     }
@@ -2068,6 +2088,13 @@ BoolCastFunctionOptional ShibokenGenerator::boolCast(const AbstractMetaClassCPtr
     return getGeneratorClassInfo(scope).boolCastFunctionO;
 }
 
+ShibokenGenerator::AttroCheck
+    ShibokenGenerator::checkAttroFunctionNeeds(const AbstractMetaClassCPtr &scope)
+{
+    Q_ASSERT(scope);
+    return getGeneratorClassInfo(scope).attroCheck;
+}
+
 // Use non-const overloads only, for example, "foo()" and "foo()const"
 // the second is removed.
 static void removeConstOverloads(AbstractMetaFunctionCList *overloads)
@@ -2243,12 +2270,8 @@ ShibokenGenerator::filterGroupedOperatorFunctions(const AbstractMetaClassCPtr &m
             funcs.erase(std::find_if(funcs.begin(), funcs.end(), isDecrementOperator));
     }
     for (const auto &func : funcs) {
-        int args;
-        if (func->isComparisonOperator()) {
-            args = -1;
-        } else {
-            args = func->arguments().size();
-        }
+        const int args = func->isComparisonOperator()
+                         ? -1 : func->arguments().size();
         auto op = std::make_pair(func->name(), args);
         results[op].append(func);
     }
@@ -2513,7 +2536,7 @@ QString ShibokenGenerator::getTypeIndexVariableName(TypeEntryCPtr type)
     // Disambiguate namespaces per module to allow for extending them.
     if (type->isNamespace()) {
         QString package = type->targetLangPackage();
-        const int dot = package.lastIndexOf(u'.');
+        const auto dot = package.lastIndexOf(u'.');
         result += QStringView{package}.right(package.size() - (dot + 1));
     }
     result += _fixedCppTypeName(type->qualifiedCppName());
@@ -2530,7 +2553,7 @@ QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType &type
     return result;
 }
 
-void collectfromTypeEntry(TypeEntryCPtr entry, QStringList &typeNames)
+void collectfromTypeEntry(const TypeEntryCPtr &entry, QStringList &typeNames)
 {
     if (entry->shouldGenerate()) {
         typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName();
@@ -2555,7 +2578,8 @@ void ShibokenGenerator::collectFullTypeNamesArray(QStringList &typeNames)
         int smartPointerCountIndex = getMaxTypeIndex();
         for (const auto &smp : api().instantiatedSmartPointers()) {
             auto entry = smp.type.typeEntry();
-            typeNames[smartPointerCountIndex] = entry->qualifiedTargetLangName();
+            typeNames[smartPointerCountIndex] =
+                smp.specialized->typeEntry()->qualifiedTargetLangName();
             ++smartPointerCountIndex;
         }
     }
index 22ee73fa2d64fcb9c9a3fecdce25aabdebea89aa..bd6f7afa9b1403b18d8d9801ba4d2b6f333ea8a4 100644 (file)
@@ -257,7 +257,7 @@ protected:
     static QString cpythonTypeNameExt(const TypeEntryCPtr &type);
     static QString cpythonTypeNameExt(const AbstractMetaType &type);
     static QString cpythonCheckFunction(TypeEntryCPtr type);
-    static QString cpythonCheckFunction(AbstractMetaType metaType);
+    static QString cpythonCheckFunction(const AbstractMetaType &metaType);
     static QString cpythonIsConvertibleFunction(const TypeEntryCPtr &type);
     static QString cpythonIsConvertibleFunction(const AbstractMetaType &metaType);
     static QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg);
@@ -268,6 +268,7 @@ protected:
     static QString cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass);
     static QString cpythonToPythonConversionFunction(const TypeEntryCPtr &type);
 
+    static QString cpythonConstructorName(const AbstractMetaClassCPtr &metaClass);
     static QString cpythonFunctionName(const AbstractMetaFunctionCPtr &func) ;
     static QString cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func);
     static QString cpythonGettersSettersDefinitionName(const AbstractMetaClassCPtr &metaClass);
@@ -373,7 +374,10 @@ private:
     static QList<AbstractMetaFunctionCList>
         getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass);
     static BoolCastFunctionOptional getBoolCast(const AbstractMetaClassCPtr &metaClass);
-    static bool classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass);
+    static bool classNeedsGetattroOverloadFunctionImpl(const FunctionGroups &functionGroups);
+    static AttroCheck checkAttroFunctionNeedsImpl(const AbstractMetaClassCPtr &metaClass,
+                                                  const FunctionGroups &functionGroups);
+    static bool isVirtualOverride(const AbstractMetaFunctionCPtr &f);
 
     QString translateTypeForWrapperMethod(const AbstractMetaType &cType,
                                           const AbstractMetaClassCPtr &context,
index b5bbb498a38b8fc25b1895ab1636c62ccdf1f205..14e38942885a264c21e74d6422cf6ab6bff9c898 100644 (file)
@@ -131,7 +131,7 @@ shiboken_compute_python_includes()
 shiboken_compute_python_libraries()
 
 if(PYTHON_LIMITED_API)
-    target_compile_definitions(libshiboken PUBLIC "-DPy_LIMITED_API=0x03060000")
+    target_compile_definitions(libshiboken PUBLIC "-DPy_LIMITED_API=0x03080000")
 endif()
 
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
index ffa73394ff722e080a7c19140d350a8e3d8c2039..61110618951d9755f56db18e7262e6f18216f1b7 100644 (file)
@@ -7,13 +7,12 @@
 #include "helper.h"
 #include "pep384ext.h"
 #include "sbkconverter.h"
-#include "sbkenum.h"
 #include "sbkerrors.h"
 #include "sbkfeature_base.h"
-#include "sbkmodule.h"
 #include "sbkstring.h"
 #include "sbkstaticstrings.h"
 #include "sbkstaticstrings_p.h"
+#include "sbktypefactory.h"
 #include "autodecref.h"
 #include "gilstate.h"
 #include <string>
@@ -39,7 +38,73 @@ namespace {
     void _destroyParentInfo(SbkObject *obj, bool keepReference);
 }
 
-static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts)
+namespace Shiboken
+{
+// Walk through the first level of non-user-type Sbk base classes relevant for
+// C++ object allocation. Return true from the predicate to terminate.
+template <class Predicate>
+bool walkThroughBases(PyTypeObject *currentType, Predicate predicate)
+{
+    PyObject *bases = currentType->tp_bases;
+    const Py_ssize_t numBases = PyTuple_Size(bases);
+    bool result = false;
+    for (Py_ssize_t i = 0; !result && i < numBases; ++i) {
+        auto *type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, i));
+        if (PyType_IsSubtype(type, SbkObject_TypeF()) != 0) {
+            result = PepType_SOTP(type)->is_user_type
+                     ? walkThroughBases(type, predicate) : predicate(type);
+        }
+    }
+    return result;
+}
+
+int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType)
+{
+    int index = -1;
+    walkThroughBases(baseType, [&index, desiredType](PyTypeObject *node) {
+        ++index;
+        return PyType_IsSubtype(node, desiredType) != 0;
+    });
+    return index;
+}
+
+int getNumberOfCppBaseClasses(PyTypeObject *baseType)
+{
+    int count = 0;
+    walkThroughBases(baseType, [&count](PyTypeObject *) {
+        ++count;
+        return false;
+    });
+    return count;
+}
+
+std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType)
+{
+    std::vector<PyTypeObject *> cppBaseClasses;
+    walkThroughBases(baseType, [&cppBaseClasses](PyTypeObject *node) {
+        cppBaseClasses.push_back(node);
+        return false;
+    });
+    return cppBaseClasses;
+}
+
+using DestructorEntries = std::vector<DestructorEntry>;
+
+DestructorEntries getDestructorEntries(SbkObject *o)
+{
+    DestructorEntries result;
+    void **cptrs = o->d->cptr;
+    walkThroughBases(Py_TYPE(o), [&result, cptrs](PyTypeObject *node) {
+        auto *sotp = PepType_SOTP(node);
+        auto index = result.size();
+        result.push_back(DestructorEntry{sotp->cpp_dtor,
+                                         cptrs[index]});
+        return false;
+    });
+    return result;
+}
+
+static void callDestructor(const DestructorEntries &dts)
 {
     for (const auto &e : dts) {
         Shiboken::ThreadStateSaver threadSaver;
@@ -48,17 +113,18 @@ static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEnt
     }
 }
 
+} // namespace Shiboken
+
 extern "C"
 {
 
 // PYSIDE-939: A general replacement for object_dealloc.
 void Sbk_object_dealloc(PyObject *self)
 {
-    if (PepRuntime_38_flag) {
-        // PYSIDE-939: Handling references correctly.
-        // This was not needed before Python 3.8 (Python issue 35810)
-        Py_DECREF(Py_TYPE(self));
-    }
+    // PYSIDE-939: Handling references correctly.
+    // This was not needed before Python 3.8 (Python issue 35810)
+    Py_DECREF(Py_TYPE(self));
+
     PepExt_TypeCallFree(self);
 }
 
@@ -181,7 +247,7 @@ PyTypeObject *SbkObjectType_TypeF(void)
 
 static PyObject *SbkObjectGetDict(PyObject *pObj, void *)
 {
-    auto ret = SbkObject_GetDict_NoRef(pObj);
+    auto *ret = SbkObject_GetDict_NoRef(pObj);
     Py_XINCREF(ret);
     return ret;
 }
@@ -196,17 +262,15 @@ static int SbkObject_tp_traverse(PyObject *self, visitproc visit, void *arg)
     auto *sbkSelf = reinterpret_cast<SbkObject *>(self);
 
     //Visit children
-    Shiboken::ParentInfo *pInfo = sbkSelf->d->parentInfo;
-    if (pInfo) {
+    if (auto *pInfo = sbkSelf->d->parentInfo) {
         for (SbkObject *c : pInfo->children)
              Py_VISIT(c);
     }
 
     //Visit refs
-    Shiboken::RefCountMap *rInfo = sbkSelf->d->referredObjects;
-    if (rInfo) {
-        for (auto it = rInfo->begin(), end = rInfo->end(); it != end; ++it)
-            Py_VISIT(it->second);
+    if (auto *rInfo = sbkSelf->d->referredObjects) {
+        for (const auto &p : *rInfo)
+            Py_VISIT(p.second);
     }
 
     if (sbkSelf->ob_dict)
@@ -296,14 +360,13 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
     // is subclassed, that dealloc func will decref (see subtype_dealloc
     // in typeobject.c in the python sources)
     auto dealloc = PyType_GetSlot(pyType, Py_tp_dealloc);
-    bool needTypeDecref = dealloc == SbkDeallocWrapper
-        || dealloc == SbkDeallocWrapperWithPrivateDtor;
-    if (PepRuntime_38_flag) {
-        // PYSIDE-939: Additional rule: Also when a subtype is heap allocated,
-        // then the subtype_dealloc deref will be suppressed, and we need again
-        // to supply a decref.
-        needTypeDecref |= (pyType->tp_base->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0;
-    }
+
+    // PYSIDE-939: Additional rule: Also when a subtype is heap allocated,
+    // then the subtype_dealloc deref will be suppressed, and we need again
+    // to supply a decref.
+    const bool needTypeDecref = dealloc == SbkDeallocWrapper
+        || dealloc == SbkDeallocWrapperWithPrivateDtor
+        || (pyType->tp_base->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0;
 
 #if defined(__APPLE__)
     // Just checking once that our assumptions are right.
@@ -342,9 +405,8 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
         if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) {
             auto &bindingManager = Shiboken::BindingManager::instance();
             if (sotp->is_multicpp) {
-                 Shiboken::DtorAccumulatorVisitor visitor(sbkObj);
-                 Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor);
-                 for (const auto &e : visitor.entries())
+                 const auto entries = Shiboken::getDestructorEntries(sbkObj);
+                 for (const auto &e : entries)
                      bindingManager.addToDeletionInMainThread(e);
             } else {
                 Shiboken::DestructorEntry e{sotp->cpp_dtor, sbkObj->d->cptr[0]};
@@ -355,17 +417,18 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
         }
     }
 
-    PyObject *error_type, *error_value, *error_traceback;
+    PyObject *error_type{};
+    PyObject *error_value{};
+    PyObject *error_traceback{};
 
     /* Save the current exception, if any. */
     PyErr_Fetch(&error_type, &error_value, &error_traceback);
 
     if (canDelete) {
         if (sotp->is_multicpp) {
-            Shiboken::DtorAccumulatorVisitor visitor(sbkObj);
-            Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor);
+            const auto entries = Shiboken::getDestructorEntries(sbkObj);
             Shiboken::Object::deallocData(sbkObj, true);
-            callDestructor(visitor.entries());
+            callDestructor(entries);
         } else {
             void *cptr = sbkObj->d->cptr[0];
             Shiboken::Object::deallocData(sbkObj, true);
@@ -384,11 +447,9 @@ static void SbkDeallocWrapperCommon(PyObject *pyObj, bool canDelete)
 
     if (needTypeDecref)
         Py_DECREF(pyType);
-    if (PepRuntime_38_flag) {
-        // PYSIDE-939: Handling references correctly.
-        // This was not needed before Python 3.8 (Python issue 35810)
-        Py_DECREF(pyType);
-    }
+    // PYSIDE-939: Handling references correctly.
+    // This was not needed before Python 3.8 (Python issue 35810)
+    Py_DECREF(pyType);
 }
 
 static inline PyObject *_Sbk_NewVarObject(PyTypeObject *type)
@@ -450,11 +511,9 @@ void SbkObjectType_tp_dealloc(PyTypeObject *sbkType)
     Py_TRASHCAN_SAFE_END(pyObj);
 #  endif
 #endif
-    if (PepRuntime_38_flag) {
-        // PYSIDE-939: Handling references correctly.
-        // This was not needed before Python 3.8 (Python issue 35810)
-        Py_DECREF(Py_TYPE(pyObj));
-    }
+    // PYSIDE-939: Handling references correctly.
+    // This was not needed before Python 3.8 (Python issue 35810)
+    Py_DECREF(Py_TYPE(pyObj));
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -471,7 +530,7 @@ PyObject *MakeQAppWrapper(PyTypeObject *type)
     static PyObject *qApp_last = nullptr;
 
     // protecting from multiple application instances
-    if (!(type == nullptr || qApp_last == Py_None)) {
+    if (type != nullptr && qApp_last != Py_None) {
         const char *res_name = qApp_last != nullptr
             ? PepType_GetNameStr(Py_TYPE(qApp_last)) : "<Unknown>";
         const char *type_name = PepType_GetNameStr(type);
@@ -509,9 +568,9 @@ static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args
     // Before we changed to heap types, it was sufficient to remove the
     // Py_TPFLAGS_BASETYPE flag. That does not work, because PySide does
     // not respect this flag itself!
-    PyObject *name;
-    PyObject *pyBases;
-    PyObject *dict;
+    PyObject *name{};
+    PyObject *pyBases{};
+    PyObject *dict{};
     static const char *kwlist[] = { "name", "bases", "dict", nullptr};
 
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!O!:sbktype", const_cast<char **>(kwlist),
@@ -520,8 +579,8 @@ static PyTypeObject *SbkObjectType_tp_new(PyTypeObject *metatype, PyObject *args
                                      &PyDict_Type, &dict))
         return nullptr;
 
-    for (int i=0, i_max=PyTuple_GET_SIZE(pyBases); i < i_max; i++) {
-        PyObject *baseType = PyTuple_GET_ITEM(pyBases, i);
+    for (Py_ssize_t i=0, i_max=PyTuple_Size(pyBases); i < i_max; i++) {
+        PyObject *baseType = PyTuple_GetItem(pyBases, i);
         if (PepExt_Type_GetNewSlot(reinterpret_cast<PyTypeObject *>(baseType)) == SbkDummyNew) {
             // PYSIDE-595: A base class does not allow inheritance.
             return reinterpret_cast<PyTypeObject *>(SbkDummyNew(metatype, args, kwds));
@@ -630,9 +689,8 @@ PyObject *SbkQApp_tp_new(PyTypeObject *subtype, PyObject *, PyObject *)
     auto *self = reinterpret_cast<SbkObject *>(obSelf);
     if (self == nullptr)
         return nullptr;
-    auto ret = _setupNew(obSelf, subtype);
-    auto priv = self->d;
-    priv->isQAppSingleton = 1;
+    auto *ret = _setupNew(obSelf, subtype);
+    self->d->isQAppSingleton = 1;
     return ret;
 }
 
@@ -650,7 +708,7 @@ PyObject *FallbackRichCompare(PyObject *self, PyObject *other, int op)
 {
     // This is a very simple implementation that supplies a simple identity.
     static const char * const opstrings[] = {"<", "<=", "==", "!=", ">", ">="};
-    PyObject *res;
+    PyObject *res{};
 
     switch (op) {
 
@@ -702,52 +760,9 @@ void _destroyParentInfo(SbkObject *obj, bool keepReference)
 
 namespace Shiboken
 {
-bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor)
-{
-    PyObject *bases = currentType->tp_bases;
-    Py_ssize_t numBases = PyTuple_GET_SIZE(bases);
-    bool result = false;
-    for (int i = 0; !result && i < numBases; ++i) {
-        auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i));
-        if (PyType_IsSubtype(type, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) {
-            result = PepType_SOTP(type)->is_user_type
-                     ? walkThroughClassHierarchy(type, visitor) : visitor->visit(type);
-        }
-    }
-    return result;
-}
 
 // Wrapper metatype and base type ----------------------------------------------------------
 
-HierarchyVisitor::HierarchyVisitor() = default;
-HierarchyVisitor::~HierarchyVisitor() = default;
-
-bool BaseCountVisitor::visit(PyTypeObject *)
-{
-    m_count++;
-    return false;
-}
-
-bool BaseAccumulatorVisitor::visit(PyTypeObject *node)
-{
-    m_bases.push_back(node);
-    return false;
-}
-
-bool GetIndexVisitor::visit(PyTypeObject *node)
-{
-    m_index++;
-    return PyType_IsSubtype(node, m_desiredType);
-}
-
-bool DtorAccumulatorVisitor::visit(PyTypeObject *node)
-{
-    auto *sotp = PepType_SOTP(node);
-    m_entries.push_back(DestructorEntry{sotp->cpp_dtor,
-                                        m_pyObject->d->cptr[m_entries.size()]});
-    return false;
-}
-
 void _initMainThreadId(); // helper.cpp
 
 static std::string msgFailedToInitializeType(const char *description)
@@ -810,26 +825,40 @@ void initShibokenSupport(PyObject *module)
 
 // setErrorAboutWrongArguments now gets overload info from the signature module.
 // Info can be nullptr and contains extra info.
-void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject *info)
-{
+void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject *info,
+                                 const char *className)
+{
+    if (className != nullptr) {
+        std::string text = std::string(className);
+        text += '.';
+        text += funcName;
+        SetError_Argument(args, text.c_str(), info);
+        return;
+    }
     SetError_Argument(args, funcName, info);
 }
 
-PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info)
+PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info,
+                               Module::TypeInitStruct initStruct)
 {
-    setErrorAboutWrongArguments(args, funcName, info);
+    const auto *className = initStruct.fullName ? initStruct.fullName : nullptr;
+    setErrorAboutWrongArguments(args, funcName, info, className);
     return {};
 }
 
-int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info)
+int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info,
+                              Module::TypeInitStruct initStruct)
 {
-    setErrorAboutWrongArguments(args, funcName, info);
+    const auto *className = initStruct.fullName ? initStruct.fullName : nullptr;
+    setErrorAboutWrongArguments(args, funcName, info, className);
     return 0;
 }
 
-int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info)
+int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info,
+                                  Module::TypeInitStruct initStruct)
 {
-    setErrorAboutWrongArguments(args, funcName, info);
+    const auto *className = initStruct.fullName ? initStruct.fullName : nullptr;
+    setErrorAboutWrongArguments(args, funcName, info, className);
     return -1;
 }
 
@@ -858,30 +887,16 @@ PyObject *checkInvalidArgumentCount(Py_ssize_t numArgs, Py_ssize_t minArgs, Py_s
     return result;
 }
 
-class FindBaseTypeVisitor : public HierarchyVisitor
-{
-public:
-    explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {}
-
-    bool visit(PyTypeObject *node) override
-    {
-        return node == m_typeToFind;
-    }
-
-private:
-    PyTypeObject *m_typeToFind;
-};
-
 std::vector<SbkObject *> splitPyObject(PyObject *pyObj)
 {
     std::vector<SbkObject *> result;
     if (PySequence_Check(pyObj)) {
         AutoDecRef lst(PySequence_Fast(pyObj, "Invalid keep reference object."));
         if (!lst.isNull()) {
-            for (Py_ssize_t i = 0, i_max = PySequence_Fast_GET_SIZE(lst.object()); i < i_max; ++i) {
-                PyObject *item = PySequence_Fast_GET_ITEM(lst.object(), i);
+            for (Py_ssize_t i = 0, i_max = PySequence_Size(lst.object()); i < i_max; ++i) {
+                Shiboken::AutoDecRef item(PySequence_GetItem(lst.object(), i));
                 if (Object::checkType(item))
-                    result.push_back(reinterpret_cast<SbkObject *>(item));
+                    result.push_back(reinterpret_cast<SbkObject *>(item.object()));
             }
         }
     } else {
@@ -912,8 +927,8 @@ bool isUserType(PyTypeObject *type)
 
 bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctorType)
 {
-    FindBaseTypeVisitor visitor(ctorType);
-    if (!walkThroughClassHierarchy(myType, &visitor)) {
+    auto findBasePred = [ctorType](PyTypeObject *type) { return type == ctorType; };
+    if (!walkThroughBases(myType, findBasePred)) {
         PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name);
         return false;
     }
@@ -988,12 +1003,12 @@ introduceWrapperType(PyObject *enclosingObject,
                      PyObject *bases,
                      unsigned wrapperFlags)
 {
-    assert(PySequence_Fast_GET_SIZE(bases) > 0);
-    typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0);
+    assert(PySequence_Size(bases) > 0);
+    typeSpec->slots[0].pfunc = PySequence_GetItem(bases, 0);
 
     auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF());
 
-    auto sotp = PepType_SOTP(type);
+    auto *sotp = PepType_SOTP(type);
     if (wrapperFlags & DeleteInMainThread)
         sotp->delete_in_main_thread = 1;
     sotp->type_behaviour = (wrapperFlags & Value) != 0
@@ -1010,8 +1025,8 @@ introduceWrapperType(PyObject *enclosingObject,
             AutoDecRef tpDict(PepType_GetDict(reinterpret_cast<PyTypeObject *>(enclosingObject)));
             return PyDict_SetItemString(tpDict, typeName, ob_type) == 0 ? type : nullptr;
         }
-        assert(PyDict_Check(enclosingObject));
-        return PyDict_SetItemString(enclosingObject, typeName, ob_type) == 0 ? type : nullptr;
+        if (PyDict_Check(enclosingObject))
+            return PyDict_SetItemString(enclosingObject, typeName, ob_type) == 0 ? type : nullptr;
     }
 
     // PyModule_AddObject steals type's reference.
@@ -1062,6 +1077,26 @@ bool hasSpecialCastFunction(PyTypeObject *sbkType)
     return d != nullptr && d->mi_specialcast != nullptr;
 }
 
+// Find whether base is a direct single line base class of type
+// (no multiple inheritance), that is, a C++ pointer cast can safely be done.
+static bool isDirectAncestor(PyTypeObject *type, PyTypeObject *base)
+{
+    if (type == base)
+        return true;
+    if (PyTuple_Size(type->tp_bases) == 0)
+        return false;
+    auto *sbkObjectType = SbkObject_TypeF();
+    auto *firstBase = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, 0));
+    return firstBase != sbkObjectType
+           && PyType_IsSubtype(type, sbkObjectType) != 0
+           && isDirectAncestor(firstBase, base);
+}
+
+bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType)
+{
+    return isDirectAncestor(targetType, baseType);
+}
+
 } // namespace ObjectType
 
 
@@ -1141,7 +1176,7 @@ bool wasCreatedByPython(SbkObject *pyObj)
 
 void callCppDestructors(SbkObject *pyObj)
 {
-    auto priv = pyObj->d;
+    auto *priv = pyObj->d;
     if (priv->isQAppSingleton && DestroyQApplication) {
         // PYSIDE-1470: Allow to destroy the application from Shiboken.
         DestroyQApplication();
@@ -1150,9 +1185,7 @@ void callCppDestructors(SbkObject *pyObj)
     PyTypeObject *type = Py_TYPE(pyObj);
     auto *sotp = PepType_SOTP(type);
     if (sotp->is_multicpp) {
-        Shiboken::DtorAccumulatorVisitor visitor(pyObj);
-        Shiboken::walkThroughClassHierarchy(type, &visitor);
-        callDestructor(visitor.entries());
+        callDestructor(getDestructorEntries(pyObj));
     } else {
         Shiboken::ThreadStateSaver threadSaver;
         threadSaver.save();
@@ -1277,10 +1310,9 @@ static void recursive_invalidate(SbkObject *self, std::set<SbkObject *> &seen)
     }
 
     // If has ref to other objects invalidate all
-    if (self->d->referredObjects) {
-        RefCountMap &refCountMap = *(self->d->referredObjects);
-        for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it)
-            recursive_invalidate(it->second, seen);
+    if (auto *rInfo = self->d->referredObjects) {
+        for (const auto &p : *rInfo)
+            recursive_invalidate(p.second, seen);
     }
 }
 
@@ -1433,33 +1465,74 @@ SbkObject *findColocatedChild(SbkObject *wrapper,
     return nullptr;
 }
 
+// Legacy, for compatibility only.
 PyObject *newObject(PyTypeObject *instanceType,
                     void *cptr,
                     bool hasOwnership,
                     bool isExactType,
                     const char *typeName)
 {
-    // Try to find the exact type of cptr.
-    if (!isExactType) {
-        if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName)) {
-            instanceType = exactType;
-        } else {
-            auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
-            if (resolved.first != nullptr) {
-                instanceType = resolved.first;
-                cptr = resolved.second;
-            }
+    return isExactType
+        ? newObjectForType(instanceType, cptr, hasOwnership)
+        : newObjectWithHeuristics(instanceType, cptr, hasOwnership, typeName);
+}
+
+static PyObject *newObjectWithHeuristicsHelper(PyTypeObject *instanceType,
+                                               PyTypeObject *exactType,
+                                               void *cptr,
+                                               bool hasOwnership)
+{
+    // Try to find the exact type of cptr. For hierarchies with
+    // non-virtual destructors, typeid() will return the base name.
+    // Try type discovery in these cases.
+    if (exactType == nullptr || exactType == instanceType) {
+        auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
+        if (resolved.first != nullptr
+            && Shiboken::ObjectType::canDowncastTo(instanceType, resolved.first)) {
+            exactType = resolved.first;
+            cptr = resolved.second;
         }
     }
 
+    return newObjectForType(exactType != nullptr ? exactType : instanceType,
+                            cptr, hasOwnership);
+}
+
+PyObject *newObjectForPointer(PyTypeObject *instanceType,
+                              void *cptr,
+                              bool hasOwnership,
+                              const char *typeName)
+{
+    // Try to find the exact type of cptr.
+    PyTypeObject *exactType = ObjectType::typeForTypeName(typeName);
+    // PYSIDE-868: In case of multiple inheritance, (for example,
+    // a function returning a QPaintDevice * from a QWidget *),
+    // use instance type to avoid pointer offset errors.
+    return exactType != nullptr && !Shiboken::ObjectType::canDowncastTo(instanceType, exactType)
+        ? newObjectForType(instanceType, cptr, hasOwnership)
+        : newObjectWithHeuristicsHelper(instanceType, exactType, cptr, hasOwnership);
+}
+
+
+PyObject *newObjectWithHeuristics(PyTypeObject *instanceType,
+                                  void *cptr,
+                                  bool hasOwnership,
+                                  const char *typeName)
+{
+    return newObjectWithHeuristicsHelper(instanceType,
+                                         ObjectType::typeForTypeName(typeName),
+                                         cptr, hasOwnership);
+}
+
+PyObject *newObjectForType(PyTypeObject *instanceType, void *cptr, bool hasOwnership)
+{
     bool shouldCreate = true;
     bool shouldRegister = true;
     SbkObject *self = nullptr;
 
+    auto &bindingManager = BindingManager::instance();
     // Some logic to ensure that colocated child field does not overwrite the parent
-    if (BindingManager::instance().hasWrapper(cptr)) {
-        SbkObject *existingWrapper = BindingManager::instance().retrieveWrapper(cptr);
-
+    if (SbkObject *existingWrapper = bindingManager.retrieveWrapper(cptr)) {
         self = findColocatedChild(existingWrapper, instanceType);
         if (self) {
             // Wrapper already registered for cptr.
@@ -1470,7 +1543,7 @@ PyObject *newObject(PyTypeObject *instanceType,
                   (!(Shiboken::Object::hasCppWrapper(existingWrapper) ||
                      Shiboken::Object::hasOwnership(existingWrapper)))) {
             // Old wrapper is likely junk, since we have ownership and it doesn't.
-            BindingManager::instance().releaseWrapper(existingWrapper);
+            bindingManager.releaseWrapper(existingWrapper);
         } else {
             // Old wrapper may be junk caused by some bug in identifying object deletion
             // but it may not be junk when a colocated field is accessed for an
@@ -1485,9 +1558,8 @@ PyObject *newObject(PyTypeObject *instanceType,
         self->d->cptr[0] = cptr;
         self->d->hasOwnership = hasOwnership;
         self->d->validCppObject = 1;
-        if (shouldRegister) {
-            BindingManager::instance().registerWrapper(self, cptr);
-        }
+        if (shouldRegister)
+            bindingManager.registerWrapper(self, cptr);
     } else {
         Py_IncRef(reinterpret_cast<PyObject *>(self));
     }
@@ -1591,8 +1663,10 @@ void setParent(PyObject *parent, PyObject *child)
      */
     if (PySequence_Check(child) && !Object::checkType(child)) {
         Shiboken::AutoDecRef seq(PySequence_Fast(child, nullptr));
-        for (Py_ssize_t i = 0, max = PySequence_Size(seq); i < max; ++i)
-            setParent(parent, PySequence_Fast_GET_ITEM(seq.object(), i));
+        for (Py_ssize_t i = 0, max = PySequence_Size(seq); i < max; ++i) {
+            Shiboken::AutoDecRef obj(PySequence_GetItem(seq.object(), i));
+            setParent(parent, obj);
+        }
         return;
     }
 
@@ -1685,7 +1759,7 @@ static inline bool isNone(const PyObject *o)
     return o == nullptr || o == Py_None;
 }
 
-static void removeRefCountKey(SbkObject *self, const char *key)
+static void removeRefCountKey(SbkObject *self, const std::string &key)
 {
     if (self->d->referredObjects) {
         const auto iterPair = self->d->referredObjects->equal_range(key);
@@ -1696,8 +1770,10 @@ static void removeRefCountKey(SbkObject *self, const char *key)
     }
 }
 
-void keepReference(SbkObject *self, const char *key, PyObject *referredObject, bool append)
+void keepReference(SbkObject *self, const char *keyC, PyObject *referredObject, bool append)
 {
+    std::string key(keyC);
+
     if (isNone(referredObject)) {
         removeRefCountKey(self, key);
         return;
@@ -1729,7 +1805,7 @@ void keepReference(SbkObject *self, const char *key, PyObject *referredObject, b
 void removeReference(SbkObject *self, const char *key, PyObject *referredObject)
 {
     if (!isNone(referredObject))
-        removeRefCountKey(self, key);
+        removeRefCountKey(self, std::string(key));
 }
 
 void clearReferences(SbkObject *self)
index 4835c481098a65f0952397761e4decd120ed868e..08a2ff99383c19b509da1a65ad378c6e65de19c0 100644 (file)
@@ -6,7 +6,7 @@
 
 #include "sbkpython.h"
 #include "shibokenmacros.h"
-#include "sbktypefactory.h"
+#include "sbkmodule.h"
 
 #include <vector>
 #include <string>
@@ -138,18 +138,20 @@ void callCppDestructor(void *cptr)
 /// setErrorAboutWrongArguments now gets overload information from the signature module.
 /// The extra info argument can contain additional data about the error.
 LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject *args, const char *funcName,
-                                                 PyObject *info);
+                                                 PyObject *info, const char *className = nullptr);
 
-/// Return values for the different retun variants.
+/// Return values for the different return variants.
 /// This is used instead of goto.
-LIBSHIBOKEN_API PyObject *returnWrongArguments(PyObject *args, const char *funcName,
-                                               PyObject *info);
+/// Either funcname should contain the full function name, or the module and class
+/// are taken from the TypeInitStruct.
+LIBSHIBOKEN_API PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info,
+                                               Module::TypeInitStruct initStruct = {nullptr, nullptr});
 
-LIBSHIBOKEN_API int returnWrongArguments_Zero(PyObject *args, const char *funcName,
-                                                    PyObject *info);
+LIBSHIBOKEN_API int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info,
+                                              Module::TypeInitStruct initStruct = {nullptr, nullptr});
 
-LIBSHIBOKEN_API int returnWrongArguments_MinusOne(PyObject *args, const char *funcName,
-                                                        PyObject *info);
+LIBSHIBOKEN_API int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info,
+                                                  Module::TypeInitStruct initStruct = {nullptr, nullptr});
 
 /// A simple special version for the end of rich comparison.
 LIBSHIBOKEN_API PyObject *returnFromRichCompare(PyObject *result);
@@ -265,6 +267,14 @@ LIBSHIBOKEN_API PyTypeObject *typeForTypeName(const char *typeName);
  * \since 5.12
  */
 LIBSHIBOKEN_API bool hasSpecialCastFunction(PyTypeObject *sbkType);
+
+/// Returns whether a C++ pointer of \p baseType can be safely downcast
+/// to \p targetType (base is a direct, single line base class of targetType).
+/// (is a direct, single-line inheritance)
+/// \param baseType   Python type of base class
+/// \param targetType Python type of derived class
+/// \since 6.8
+LIBSHIBOKEN_API bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType);
 }
 
 namespace Object {
@@ -297,7 +307,8 @@ LIBSHIBOKEN_API SbkObject *findColocatedChild(SbkObject *wrapper,
                                               const PyTypeObject *instanceType);
 
 /**
- *  Bind a C++ object to Python.
+ *  Bind a C++ object to Python. Forwards to
+ *  newObjectWithHeuristics(), newObjectForType() depending on \p isExactType.
  * \param instanceType equivalent Python type for the C++ object.
  * \param hasOwnership if true, Python will try to delete the underlying C++ object when there's no more refs.
  * \param isExactType if false, Shiboken will use some heuristics to detect the correct Python type of this C++
@@ -311,6 +322,40 @@ LIBSHIBOKEN_API PyObject *newObject(PyTypeObject *instanceType,
                                     bool isExactType = false,
                                     const char *typeName = nullptr);
 
+/// Bind a C++ object to Python for polymorphic pointers. Calls
+/// newObjectWithHeuristics() with an additional check for multiple
+/// inheritance, in which case it will fall back to instanceType.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying C++ object
+///                     when there's no more refs.
+/// \param typeName     If non-null, this will be used as helper to find the correct
+///                     Python type for this object (obtained by typeid().name().
+LIBSHIBOKEN_API PyObject *newObjectForPointer(PyTypeObject *instanceType,
+                                              void *cptr,
+                                              bool hasOwnership = true,
+                                              const char *typeName = nullptr);
+
+/// Bind a C++ object to Python using some heuristics to detect the correct
+/// Python type of this C++ object. In any case \p instanceType must be provided;
+/// it'll be used as search starting point and as fallback.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying C++ object
+///                     C++ object when there are no more references.
+///                     when there's no more refs.
+/// \param typeName     If non-null, this will be used as helper to find the correct
+///                     Python type for this object (obtained by typeid().name().
+LIBSHIBOKEN_API PyObject *newObjectWithHeuristics(PyTypeObject *instanceType,
+                                                  void *cptr,
+                                                  bool hasOwnership = true,
+                                                  const char *typeName = nullptr);
+
+/// Bind a C++ object to Python using the given type.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying
+///                     C++ object when there are no more references.
+LIBSHIBOKEN_API PyObject *newObjectForType(PyTypeObject *instanceType,
+                                           void *cptr, bool hasOwnership = true);
+
 /**
  *  Changes the valid flag of a PyObject, invalid objects will raise an exception when someone tries to access it.
  */
index 526bf9fa3ee903b9e3107bc2c68663ea85687274..fb9140793a365ec79f50b5a8801d45a24e92d915 100644 (file)
@@ -114,7 +114,8 @@ struct SbkObjectTypePrivate
 
     /// True if this type holds two or more C++ instances, e.g.: a Python class which inherits from two C++ classes.
     unsigned int is_multicpp : 1;
-    /// True if this type was defined by the user.
+    /// True if this type was defined by the user (a class written in Python inheriting
+    /// a class provided by a Shiboken binding).
     unsigned int is_user_type : 1;
     /// Tells is the type is a value type or an object-type, see BEHAVIOUR_ *constants.
     unsigned int type_behaviour : 2;
@@ -142,107 +143,7 @@ struct DestructorEntry
  **/
 std::vector<SbkObject *> splitPyObject(PyObject *pyObj);
 
-/**
-*   Visitor class used by walkOnClassHierarchy function.
-*/
-class HierarchyVisitor
-{
-public:
-    HierarchyVisitor(const HierarchyVisitor &) = delete;
-    HierarchyVisitor(HierarchyVisitor &&) = delete;
-    HierarchyVisitor &operator=(const HierarchyVisitor &) = delete;
-    HierarchyVisitor &operator=(HierarchyVisitor &&) = delete;
-
-    HierarchyVisitor();
-    virtual ~HierarchyVisitor();
-
-    virtual bool visit(PyTypeObject *node) = 0; // return true to terminate
-};
-
-class BaseCountVisitor : public HierarchyVisitor
-{
-public:
-    bool visit(PyTypeObject *) override;
-
-    int count() const { return m_count; }
-
-private:
-    int m_count = 0;
-};
-
-class BaseAccumulatorVisitor : public HierarchyVisitor
-{
-public:
-    using Result = std::vector<PyTypeObject *>;
-
-    bool visit(PyTypeObject *node) override;
-
-    Result bases() const { return m_bases; }
-
-private:
-    Result m_bases;
-};
-
-class GetIndexVisitor : public HierarchyVisitor
-{
-public:
-    explicit GetIndexVisitor(PyTypeObject *desiredType) : m_desiredType(desiredType) {}
-
-    bool visit(PyTypeObject *node) override;
-
-    int index() const { return m_index; }
-
-private:
-    int m_index = -1;
-    PyTypeObject *m_desiredType;
-};
-
-/// Collect destructors and C++ instances of each C++ object held by a Python
-/// object
-class DtorAccumulatorVisitor : public HierarchyVisitor
-{
-public:
-    explicit DtorAccumulatorVisitor(SbkObject *pyObj) : m_pyObject(pyObj) {}
-
-    bool visit(PyTypeObject *node) override;
-
-    using DestructorEntries = std::vector<DestructorEntry>;
-
-    const DestructorEntries &entries() const { return m_entries; }
-
-private:
-    DestructorEntries m_entries;
-    SbkObject *m_pyObject;
-};
-
-/// \internal Internal function used to walk on classes inheritance trees.
-/**
-*   Walk on class hierarchy using a DFS algorithm.
-*   For each pure Shiboken type found, HierarchyVisitor::visit is called and the algorithm
-*   considers all children of this type as visited.
-*/
-bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor);
-
-inline int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType)
-{
-    GetIndexVisitor visitor(desiredType);
-    walkThroughClassHierarchy(baseType, &visitor);
-    return visitor.index();
-}
-
-inline int getNumberOfCppBaseClasses(PyTypeObject *baseType)
-{
-    BaseCountVisitor visitor;
-    walkThroughClassHierarchy(baseType, &visitor);
-    return visitor.count();
-}
-
-inline std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType)
-{
-    BaseAccumulatorVisitor visitor;
-    walkThroughClassHierarchy(baseType, &visitor);
-    return visitor.bases();
-}
+int getNumberOfCppBaseClasses(PyTypeObject *baseType);
 
 namespace Object
 {
index 83c927ae5c611800438a174fb440d3c700635a22..89990544eb9cf01c8d65b2ec5af8a9848d912e63 100644 (file)
@@ -230,10 +230,9 @@ void BindingManager::BindingManagerPrivate::assignWrapper(SbkObject *wrapper, co
     }
 }
 
-BindingManager::BindingManager()
+BindingManager::BindingManager() :
+    m_d(new BindingManager::BindingManagerPrivate)
 {
-    m_d = new BindingManager::BindingManagerPrivate;
-
 #ifdef SHIBOKEN_INSTALL_FREE_DEBUG_HOOK
     debugInstallFreeHook();
 #endif
@@ -354,7 +353,7 @@ PyObject *BindingManager::getOverride(const void *cptr,
         return method;
     }
 
-    PyObject *method = PyObject_GetAttr(reinterpret_cast<PyObject *>(wrapper), pyMethodName);
+    PyObject *method = PyObject_GetAttr(obWrapper, pyMethodName);
 
     PyObject *function = nullptr;
 
@@ -363,7 +362,7 @@ PyObject *BindingManager::getOverride(const void *cptr,
     if (method) {
         // PYSIDE-535: This macro is redefined in a compatible way in pep384
         if (PyMethod_Check(method)) {
-            if (PyMethod_GET_SELF(method) == reinterpret_cast<PyObject *>(wrapper)) {
+            if (PyMethod_GET_SELF(method) == obWrapper) {
                 function = PyMethod_GET_FUNCTION(method);
             } else {
                 Py_DECREF(method);
@@ -376,7 +375,7 @@ PyObject *BindingManager::getOverride(const void *cptr,
             // Not retaining a reference inline with what PyMethod_GET_SELF does.
             Py_DECREF(im_self);
 
-            if (im_self == reinterpret_cast<PyObject *>(wrapper)) {
+            if (im_self == obWrapper) {
                 function = PyObject_GetAttr(method, PyName::im_func());
                 // Not retaining a reference inline with what PyMethod_GET_FUNCTION does.
                 Py_DECREF(function);
@@ -391,20 +390,17 @@ PyObject *BindingManager::getOverride(const void *cptr,
     }
 
     if (method != nullptr) {
-        PyObject *defaultMethod{};
         PyObject *mro = Py_TYPE(wrapper)->tp_mro;
 
-        int size = PyTuple_GET_SIZE(mro);
+        int size = PyTuple_Size(mro);
         bool defaultFound = false;
         // The first class in the mro (index 0) is the class being checked and it should not be tested.
         // The last class in the mro (size - 1) is the base Python object class which should not be tested also.
         for (int idx = 1; idx < size - 1; ++idx) {
-            auto *parent = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
-            AutoDecRef tpDict(PepType_GetDict(parent));
-            auto *parentDict = tpDict.object();
+            auto *parent = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
+            AutoDecRef parentDict(PepType_GetDict(parent));
             if (parentDict) {
-                defaultMethod = PyDict_GetItem(parentDict, pyMethodName);
-                if (defaultMethod) {
+                if (PyObject *defaultMethod = PyDict_GetItem(parentDict.object(), pyMethodName)) {
                     defaultFound = true;
                     if (function != defaultMethod)
                         return method;
@@ -489,8 +485,8 @@ static bool isPythonType(PyTypeObject *type)
     return !ObjectType::checkType(type) || ObjectType::isUserType(type);
 }
 
-bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
-                                       const char *fullName)
+static bool _callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+                               std::string_view className)
 {
     using Shiboken::AutoDecRef;
 
@@ -504,28 +500,26 @@ bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
 
     auto *startType = Py_TYPE(self);
     auto *mro = startType->tp_mro;
-    Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
-    auto classNameLen = std::strrchr(fullName, '.') - fullName;
+    Py_ssize_t idx = 0;
+    const Py_ssize_t n = PyTuple_Size(mro);
     /* No need to check the last one: it's gonna be skipped anyway.  */
-    for (idx = 0; idx + 1 < n; ++idx) {
-        auto *lookType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
-        const char *lookName = lookType->tp_name;
-        auto lookLen = long(std::strlen(lookName));
-        if (std::strncmp(lookName, fullName, classNameLen) == 0 && lookLen == classNameLen)
+    for ( ; idx + 1 < n; ++idx) {
+        auto *lookType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
+        if (className == lookType->tp_name)
             break;
     }
     // We are now at the first non-Python class `QObject`.
     // mro: ('C', 'A', 'QObject', 'Object', 'B', 'object')
     // We want to catch class `B` and call its `__init__`.
     for (idx += 1; idx + 1 < n; ++idx) {
-        auto *t = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+        auto *t = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
         if (isPythonType(t))
             break;
     }
     if (idx >= n)
         return false;
 
-    auto *obSubType = PyTuple_GET_ITEM(mro, idx);
+    auto *obSubType = PyTuple_GetItem(mro, idx);
     auto *subType = reinterpret_cast<PyTypeObject *>(obSubType);
     if (subType == &PyBaseObject_Type)
         return false;
@@ -539,11 +533,29 @@ bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
     AutoDecRef newArgs(PyTuple_New(1));
     auto *newArgsOb = newArgs.object();
     Py_INCREF(self);
-    PyTuple_SET_ITEM(newArgsOb, 0, self);
+    PyTuple_SetItem(newArgsOb, 0, self);
     // Note: This can fail, so please always check the error status.
     AutoDecRef result(PyObject_Call(func, newArgs, kwds));
     return true;
 }
 
-} // namespace Shiboken
+bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+                       const char *fullName)
+{
+    // fullName is the full dotted name of module, class and function.
+    // We need to cut off the rightmost field to get the module.class name.
+    auto className = std::string_view(fullName);
+    auto pos = className.rfind('.');
+    assert(pos != std::string_view::npos);
+    className = className.substr(0, pos);
+    return _callInheritedInit(self, args, kwds, className);
+}
 
+bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+                       Module::TypeInitStruct typeStruct)
+{
+    // TypeInitStruct must contain the module.class name.
+    return _callInheritedInit(self, args, kwds, typeStruct.fullName);
+}
+
+} // namespace Shiboken
index 54c4e486a33aced79ad4b9a4a953fe71be85d792..300783e7a4a8bf331386dbaf276494e55aa961e8 100644 (file)
@@ -86,8 +86,9 @@ private:
 
 LIBSHIBOKEN_API bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
                                        const char *fullName);
+LIBSHIBOKEN_API bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+                                       Module::TypeInitStruct typeStruct);
 
 } // namespace Shiboken
 
 #endif // BINDINGMANAGER_H
-
index a058fd372ef78746ace444829a21a715af0b5f14..eb643efcea79d79b3b75010662b5c24e8982da70 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 embedding_generator.py
@@ -38,7 +39,7 @@ assert (build_script_dir / "build_scripts").exists()
 
 sys.path.insert(0, os.fspath(build_script_dir))
 
-from build_scripts import utils
+from build_scripts import utils  # noqa: E402
 
 
 def runpy(cmd, **kw):
@@ -156,8 +157,7 @@ def _embed_bytefile(fin, fout, is_text):
          * {remark}.
          */
          """), file=fout)
-    headsize = ( 0 if is_text else
-                16 if sys.version_info >= (3, 7) else 12 if sys.version_info >= (3, 3) else 8)
+    headsize = 0 if is_text else 16
     binstr = fin.read()[headsize:]
     if is_text:
         try:
@@ -193,7 +193,7 @@ def _embed_bytefile(fin, fout, is_text):
     print(file=fout)
     use_ord = sys.version_info[0] == 2
     for i in range(0, len(binstr), 16):
-        for c in bytes(binstr[i : i + 16]):
+        for c in bytes(binstr[i: i + 16]):
             ord_c = ord(c) if use_ord else c
             print(f"{ord_c:#4},", file=fout, end="")
         print(file=fout)
index a58ce6e4f3e10b34eb9431d14bbe1091ec3b3e1d..8f7be6437300325204d5a0741a25f201278ac2b5 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 module_collector.py
index 37f95cd358710f77ced85abb4ab96a52fa2104f5..7458500b3a1459bbbd7777ebb25992fdc4e3eb2c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 signature_bootstrap.py
@@ -166,7 +167,7 @@ def prepare_zipfile():
     return sys.meta_path, EmbeddableZipImporter(vzip)
 
 
-class EmbeddableZipImporter(object):
+class EmbeddableZipImporter:
 
     def __init__(self, zip_file):
         def p2m(filename):
index 8daa93b8b697fe942090c12857bf1b5a0ab1cbb5..4414de648e428ae31b9a1203b4c99fce4eeed133 100644 (file)
@@ -35,4 +35,3 @@ void GilState::abandon()
 }
 
 } // namespace Shiboken
-
index abad9d955e0d1fba224b5d7ef029607c25ef78fe..1714d77a53519334f723bcf808f9426471a7e1c1 100644 (file)
@@ -30,4 +30,3 @@ private:
 } // namespace Shiboken
 
 #endif // GILSTATE_H
-
index 46af68956e4d0451208126da722004c195881de0..dc81bbb29d89493975ca7781b33b4e1df91dfb72 100644 (file)
@@ -148,10 +148,9 @@ static void formatPySequence(PyObject *obj, std::ostream &str)
         if (i)
             str << ", ";
         str << '(';
-        PyObject *item = PySequence_GetItem(obj, i);
+        Shiboken::AutoDecRef item(PySequence_GetItem(obj, i));
         formatPyObject(item, str);
         str << ')';
-        Py_XDECREF(item);
     }
     if (printSize < size)
         str << ",...";
@@ -482,8 +481,8 @@ bool listToArgcArgv(PyObject *argList, int *argc, char ***argv, const char *defa
 
     // Check all items
     Shiboken::AutoDecRef args(PySequence_Fast(argList, nullptr));
-    int numArgs = int(PySequence_Fast_GET_SIZE(argList));
-    for (int i = 0; i < numArgs; ++i) {
+    Py_ssize_t numArgs = PySequence_Size(argList);
+    for (Py_ssize_t i = 0; i < numArgs; ++i) {
         PyObject *item = PyList_GET_ITEM(args.object(), i);
         if (!PyBytes_Check(item) && !PyUnicode_Check(item))
             return false;
@@ -525,11 +524,11 @@ int *sequenceToIntArray(PyObject *obj, bool zeroTerminated)
     if (seq.isNull())
         return nullptr;
 
-    Py_ssize_t size = PySequence_Fast_GET_SIZE(seq.object());
+    Py_ssize_t size = PySequence_Size(seq.object());
     int *array = new int[size + (zeroTerminated ? 1 : 0)];
 
     for (int i = 0; i < size; i++) {
-        PyObject *item = PySequence_Fast_GET_ITEM(seq.object(), i);
+        Shiboken::AutoDecRef item(PySequence_GetItem(seq.object(), i));
         if (!PyLong_Check(item)) {
             PyErr_SetString(PyExc_TypeError, "Sequence of ints expected");
             delete[] array;
index 021c53d169ecd77d6db7fc37b4945325ab7937e3..0ce53d3a775ba78841b578c461180866296791d0 100644 (file)
@@ -86,4 +86,6 @@ inline void PepExt_TypeCallFree(PyObject *object)
     PepExt_Type_GetFreeSlot(Py_TYPE(object))(object);
 }
 
+LIBSHIBOKEN_API bool PepExt_Weakref_IsAlive(PyObject *weakRef);
+
 #endif // PEP384EXT_H
index 2b04af8572879eab630325e0d0e3179175a438b5..dc067cd5247996a8db7c5470bcfa580d401fe8ec 100644 (file)
@@ -177,36 +177,27 @@ check_PyTypeObject_valid()
 static PyObject *
 find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
 {
-    Py_ssize_t i, n;
-    PyObject *mro, *res, *base;
-
-    /* Look in tp_dict of types in MRO */
-    mro = type->tp_mro;
-
-    res = nullptr;
-    /* Keep a strong reference to mro because type->tp_mro can be replaced
-       during dict lookup, e.g. when comparing to non-string keys. */
-    Py_INCREF(mro);
-    assert(PyTuple_Check(mro));
-    n = PyTuple_GET_SIZE(mro);
-    for (i = 0; i < n; i++) {
-        base = PyTuple_GET_ITEM(mro, i);
+    *error = 0;
+    /* Look in tp_dict of types in MRO. Keep a strong reference to mro because
+     * type->tp_mro can be replaced during dict lookup, e.g. when comparing to
+     * non-string keys. */
+    assert(PyTuple_Check(type->tp_mro));
+    Py_INCREF(type->tp_mro);
+    Shiboken::AutoDecRef mro(type->tp_mro);
+    for (Py_ssize_t i = 0, n = PyTuple_Size(mro.object()); i < n; ++i) {
+        auto *base = PyTuple_GetItem(mro.object(), i);
         assert(PyType_Check(base));
         auto *type = reinterpret_cast<PyTypeObject *>(base);
         Shiboken::AutoDecRef dict(PepType_GetDict(type));
         assert(!dict.isNull() && PyDict_Check(dict.object()));
-        res = PyDict_GetItem(dict.object(), name);
-        if (res != nullptr)
-            break;
+        if (auto *res = PyDict_GetItem(dict.object(), name))
+            return res;
         if (PyErr_Occurred()) {
             *error = -1;
-            goto done;
+            return nullptr;
         }
     }
-    *error = 0;
-done:
-    Py_DECREF(mro);
-    return res;
+    return nullptr;
 }
 
 /* Internal API to look for a name through the MRO.
@@ -214,26 +205,20 @@ done:
 PyObject *
 _PepType_Lookup(PyTypeObject *type, PyObject *name)
 {
-    PyObject *res;
-    int error;
-
     /* We may end up clearing live exceptions below, so make sure it's ours. */
     assert(!PyErr_Occurred());
 
-    res = find_name_in_mro(type, name, &error);
-    /* Only put NULL results into cache if there was no error. */
+    int error{};
+    auto *res = find_name_in_mro(type, name, &error);
+    /* Only put nullptr results into cache if there was no error. */
     if (error) {
-        /* It's not ideal to clear the error condition,
-           but this function is documented as not setting
-           an exception, and I don't want to change that.
-           E.g., when PyType_Ready() can't proceed, it won't
-           set the "ready" flag, so future attempts to ready
-           the same type will call it again -- hopefully
-           in a context that propagates the exception out.
-        */
-        if (error == -1) {
+        /* It's not ideal to clear the error condition, but this function is
+         * documented as not setting an exception, and I don't want to change
+         * that. E.g., when PyType_Ready() can't proceed, it won't set the
+         * "ready" flag, so future attempts to ready the same type will call
+         * it again -- hopefully in a context that propagates the exception out. */
+        if (error == -1)
             PyErr_Clear();
-        }
         return nullptr;
     }
     return res;
@@ -442,13 +427,11 @@ const char *_PepUnicode_AsString(PyObject *str)
  */
 #ifdef Py_LIMITED_API
 
-static PyObject *sys_flags = nullptr;
-
 int
 Pep_GetFlag(const char *name)
 {
+    static PyObject *sys_flags = nullptr;
     static int initialized = 0;
-    int ret = -1;
 
     if (!initialized) {
         sys_flags = PySys_GetObject("flags");
@@ -457,14 +440,11 @@ Pep_GetFlag(const char *name)
         initialized = 1;
     }
     if (sys_flags != nullptr) {
-        PyObject *ob_ret = PyObject_GetAttrString(sys_flags, name);
-        if (ob_ret != nullptr) {
-            long long_ret = PyLong_AsLong(ob_ret);
-            Py_DECREF(ob_ret);
-            ret = (int) long_ret;
-        }
+        Shiboken::AutoDecRef ob_ret(PyObject_GetAttrString(sys_flags, name));
+        if (!ob_ret.isNull())
+            return int(PyLong_AsLong(ob_ret.object()));
     }
-    return ret;
+    return -1;
 }
 
 int
@@ -496,6 +476,31 @@ PyObject *PepErr_GetRaisedException()
     PyErr_Restore(type, value, traceback);
     return value;
 }
+
+struct PepException_HEAD
+{
+    PyObject_HEAD
+    PyObject *x1; // dict
+    PyObject *args;
+};
+
+// PyException_GetArgs/PyException_SetArgs were added to the stable API in 3.12
+PyObject *PepException_GetArgs(PyObject *ex)
+{
+    auto *h = reinterpret_cast<PepException_HEAD *>(ex);
+    Py_XINCREF(h->args);
+    return h->args;
+}
+
+LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args)
+{
+    auto *h = reinterpret_cast<PepException_HEAD *>(ex);
+    Py_XINCREF(args);
+    auto *old = h->args; // Py_XSETREF()
+    h->args = args;
+    Py_XDECREF(old);
+
+}
 #endif // Limited or < 3.12
 
 /*****************************************************************************
@@ -508,17 +513,9 @@ PyObject *PepErr_GetRaisedException()
 int
 PepCode_Get(PepCodeObject *co, const char *name)
 {
-    PyObject *ob = reinterpret_cast<PyObject *>(co);
-    PyObject *ob_ret;
-    int ret = -1;
-
-    ob_ret = PyObject_GetAttrString(ob, name);
-    if (ob_ret != nullptr) {
-        long long_ret = PyLong_AsLong(ob_ret);
-        Py_DECREF(ob_ret);
-        ret = (int) long_ret;
-    }
-    return ret;
+    auto *ob = reinterpret_cast<PyObject *>(co);
+    Shiboken::AutoDecRef ob_ret(PyObject_GetAttrString(ob, name));
+    return ob_ret.isNull() ? -1 : int(PyLong_AsLong(ob_ret.object()));
 }
 
 int PepCode_Check(PyObject *o)
@@ -583,32 +580,24 @@ init_DateTime(void)
 int
 PyDateTime_Get(PyObject *ob, const char *name)
 {
-    PyObject *ob_ret;
-    int ret = -1;
-
-    ob_ret = PyObject_GetAttrString(ob, name);
-    if (ob_ret != nullptr) {
-        long long_ret = PyLong_AsLong(ob_ret);
-        Py_DECREF(ob_ret);
-        ret = (int) long_ret;
-    }
-    return ret;
+    Shiboken::AutoDecRef ob_ret(PyObject_GetAttrString(ob, name));
+    return ob_ret.isNull() ? -1 : int(PyLong_AsLong(ob_ret.object()));
 }
 
 PyObject *
 PyDate_FromDate(int year, int month, int day)
 {
-    return PyObject_CallFunction((PyObject *)PyDateTimeAPI->DateType,
-                                 (char *)"(iii)", year, month, day);
+    auto *ob = reinterpret_cast<PyObject *>(PyDateTimeAPI->DateType);
+    return PyObject_CallFunction(ob, "(iii)", year, month, day);
 }
 
 PyObject *
 PyDateTime_FromDateAndTime(int year, int month, int day,
                            int hour, int min, int sec, int usec)
 {
-    return PyObject_CallFunction((PyObject *)PyDateTimeAPI->DateTimeType,
-                                 (char *)"(iiiiiii)", year, month, day,
-                                  hour, min, sec, usec);
+    auto *ob = reinterpret_cast<PyObject *>(PyDateTimeAPI->DateTimeType);
+    return PyObject_CallFunction(ob, "(iiiiiii)", year, month, day,
+                                 hour, min, sec, usec);
 }
 
 PyObject *
@@ -630,14 +619,8 @@ PyTime_FromTime(int hour, int min, int sec, int usec)
 PyObject *
 PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
 {
-    PyObject *code = Py_CompileString(str, "pyscript", start);
-    PyObject *ret = nullptr;
-
-    if (code != nullptr) {
-        ret = PyEval_EvalCode(code, globals, locals);
-    }
-    Py_XDECREF(code);
-    return ret;
+    Shiboken::AutoDecRef code(Py_CompileString(str, "pyscript", start));
+    return code.isNull() ? nullptr : PyEval_EvalCode(code.object(), globals, locals);
 }
 
 #endif // Py_LIMITED_API
@@ -653,10 +636,10 @@ PyTypeObject *PepMethod_TypePtr = nullptr;
 
 static PyTypeObject *getMethodType(void)
 {
-    static const char prog[] =
-        "class _C:\n"
-        "    def _m(self): pass\n"
-        "result = type(_C()._m)\n";
+    static const char prog[] = R"(class _C:
+    def _m(self): pass
+result = type(_C()._m)
+)";
     return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog));
 }
 
@@ -664,16 +647,15 @@ static PyTypeObject *getMethodType(void)
 PyObject *
 PyMethod_New(PyObject *func, PyObject *self)
 {
-    return PyObject_CallFunction((PyObject *)PepMethod_TypePtr,
-                                 (char *)"(OO)", func, self);
+    auto *ob = reinterpret_cast<PyObject *>(PepMethod_TypePtr);
+    return PyObject_CallFunction(ob, "(OO)", func, self);
 }
 
 PyObject *
 PyMethod_Function(PyObject *im)
 {
-    PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::func());
-
     // We have to return a borrowed reference.
+    PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::func());
     Py_DECREF(ret);
     return ret;
 }
@@ -681,10 +663,9 @@ PyMethod_Function(PyObject *im)
 PyObject *
 PyMethod_Self(PyObject *im)
 {
-    PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::self());
-
     // We have to return a borrowed reference.
     // If we don't obey that here, then we get a test error!
+    PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::self());
     Py_DECREF(ret);
     return ret;
 }
@@ -700,10 +681,8 @@ PyMethod_Self(PyObject *im)
 PyObject *
 PepFunction_Get(PyObject *ob, const char *name)
 {
-    PyObject *ret;
-
     // We have to return a borrowed reference.
-    ret = PyObject_GetAttrString(ob, name);
+    PyObject *ret = PyObject_GetAttrString(ob, name);
     Py_XDECREF(ret);
     return ret;
 }
@@ -718,7 +697,7 @@ static PyTypeObject *getFunctionType(void)
         "from types import FunctionType as result\n";
     return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog));
 }
-#endif // Py_LIMITED_API || Python 2
+#endif // Py_LIMITED_API
 
 /*****************************************************************************
  *
@@ -799,9 +778,7 @@ PepType_GetNameStr(PyTypeObject *type)
 {
     const char *ret = type->tp_name;
     const char *nodots = std::strrchr(ret, '.');
-    if (nodots)
-        ret = nodots + 1;
-    return ret;
+    return nodots != nullptr ? nodots + 1 : ret;
 }
 
 // PYSIDE-2264: Find the _functools or functools module and retrieve the
@@ -839,22 +816,20 @@ Pep_GetPartialFunction(void)
 PyObject *
 PyImport_GetModule(PyObject *name)
 {
-    PyObject *m;
     PyObject *modules = PyImport_GetModuleDict();
-    if (modules == NULL) {
+    if (modules == nullptr) {
         PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules");
-        return NULL;
+        return nullptr;
     }
     Py_INCREF(modules);
+    PyObject *m{};
     if (PyDict_CheckExact(modules)) {
         m = PyDict_GetItemWithError(modules, name);  /* borrowed */
         Py_XINCREF(m);
-    }
-    else {
+    } else {
         m = PyObject_GetItem(modules, name);
-        if (m == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
+        if (m == nullptr && PyErr_ExceptionMatches(PyExc_KeyError))
             PyErr_Clear();
-        }
     }
     Py_DECREF(modules);
     return m;
@@ -871,15 +846,13 @@ PepRun_GetResult(const char *command)
     /*
      * Evaluate a string and return the variable `result`
      */
-    PyObject *d, *v, *res;
-
-    d = PyDict_New();
+    PyObject *d = PyDict_New();
     if (d == nullptr
         || PyDict_SetItem(d, Shiboken::PyMagicName::builtins(), PyEval_GetBuiltins()) < 0) {
         return nullptr;
     }
-    v = PyRun_String(command, Py_file_input, d, d);
-    res = v ? PyDict_GetItem(d, Shiboken::PyName::result()) : nullptr;
+    PyObject *v = PyRun_String(command, Py_file_input, d, d);
+    PyObject *res = v ? PyDict_GetItem(d, Shiboken::PyName::result()) : nullptr;
     Py_XDECREF(v);
     Py_DECREF(d);
     return res;
@@ -887,7 +860,7 @@ PepRun_GetResult(const char *command)
 
 PyTypeObject *PepType_Type_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
 {
-    auto ret = PyType_Type.tp_new(metatype, args, kwds);
+    auto *ret = PyType_Type.tp_new(metatype, args, kwds);
     return reinterpret_cast<PyTypeObject *>(ret);
 }
 
@@ -897,12 +870,6 @@ PyTypeObject *PepType_Type_tp_new(PyTypeObject *metatype, PyObject *args, PyObje
  *
  */
 
-#ifdef Py_LIMITED_API
-// We keep these definitions local, because they don't work in Python 2.
-# define PyUnicode_GET_LENGTH(op)    PyUnicode_GetLength((PyObject *)(op))
-# define PyUnicode_READ_CHAR(u, i)   PyUnicode_ReadChar((PyObject *)(u), (i))
-#endif // Py_LIMITED_API
-
 PyObject *
 _Pep_PrivateMangle(PyObject *self, PyObject *name)
 {
@@ -911,15 +878,15 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name)
      * This function is modelled after _Py_Mangle, but is optimized
      * a little for our purpose.
      */
-    if (PyUnicode_READ_CHAR(name, 0) != '_' ||
-        PyUnicode_READ_CHAR(name, 1) != '_') {
+    if (PyUnicode_ReadChar(name, 0) != '_' ||
+        PyUnicode_ReadChar(name, 1) != '_') {
         Py_INCREF(name);
         return name;
     }
-    size_t nlen = PyUnicode_GET_LENGTH(name);
+    const Py_ssize_t nlen = PyUnicode_GetLength(name);
     /* Don't mangle __id__ or names with dots. */
-    if ((PyUnicode_READ_CHAR(name, nlen-1) == '_' &&
-         PyUnicode_READ_CHAR(name, nlen-2) == '_') ||
+    if ((PyUnicode_ReadChar(name, nlen-1) == '_' &&
+         PyUnicode_ReadChar(name, nlen-2) == '_') ||
         PyUnicode_FindChar(name, '.', 0, nlen, 1) != -1) {
         Py_INCREF(name);
         return name;
@@ -930,10 +897,10 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name)
     // PYSIDE-1436: _Py_Mangle is no longer exposed; implement it always.
     // The rest of this function is our own implementation of _Py_Mangle.
     // Please compare the original function in compile.c .
-    size_t plen = PyUnicode_GET_LENGTH(privateobj.object());
+    Py_ssize_t plen = PyUnicode_GetLength(privateobj.object());
     /* Strip leading underscores from class name */
-    size_t ipriv = 0;
-    while (PyUnicode_READ_CHAR(privateobj.object(), ipriv) == '_')
+    Py_ssize_t ipriv = 0;
+    while (PyUnicode_ReadChar(privateobj.object(), ipriv) == '_')
         ipriv++;
     if (ipriv == plen) {
         Py_INCREF(name);
@@ -946,10 +913,11 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name)
                         "private identifier too large to be mangled");
         return nullptr;
     }
-    size_t const amount = ipriv + 1 + plen + nlen;
-    size_t const big_stack = 1000;
+    const Py_ssize_t amount = ipriv + 1 + plen + nlen;
+    const Py_ssize_t big_stack = 1000;
     wchar_t bigbuf[big_stack];
-    wchar_t *resbuf = amount <= big_stack ? bigbuf : (wchar_t *)malloc(sizeof(wchar_t) * amount);
+    wchar_t *resbuf = amount <= big_stack
+        ? bigbuf : reinterpret_cast<wchar_t *>(malloc(sizeof(wchar_t) * amount));
     if (!resbuf)
         return nullptr;
     /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */
@@ -970,19 +938,6 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name)
  *
  */
 
-int PepRuntime_38_flag = 0;
-
-static void
-init_PepRuntime()
-{
-    // We expect a string of the form "\d\.\d+\."
-    const char *version = Py_GetVersion();
-    if (version[0] < '3')
-        return;
-    if (std::atoi(version + 2) >= 8)
-        PepRuntime_38_flag = 1;
-}
-
 static long _GetPepRuntimeVersion()
 {
     auto *version = PySys_GetObject("version_info");
@@ -1122,10 +1077,8 @@ SbkEnumTypePrivate *PepType_SETP(SbkEnumType *enumType)
     if (enumType == SETP_key)
         return SETP_value;
     auto it = SETP_extender.find(enumType);
-    if (it == SETP_extender.end()) {
-        it = SETP_extender.insert({enumType, {}}).first;
-        memset(&it->second, 0, sizeof(SbkEnumTypePrivate));
-    }
+    if (it == SETP_extender.end())
+        it = SETP_extender.insert({enumType, SbkEnumTypePrivate{nullptr, nullptr}}).first;
     SETP_key = enumType;
     SETP_value = &it->second;
     return SETP_value;
@@ -1278,7 +1231,6 @@ static inline void *PepType_ExTP(PyTypeObject *type, size_t size)
 void
 Pep384_Init()
 {
-    init_PepRuntime();
 #ifdef Py_LIMITED_API
     check_PyTypeObject_valid();
     Pep_GetVerboseFlag();
@@ -1292,3 +1244,17 @@ Pep384_Init()
 }
 
 } // extern "C"
+
+LIBSHIBOKEN_API bool PepExt_Weakref_IsAlive(PyObject *weakRef)
+{
+#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030D0000
+    return PyWeakref_GetObject(weakRef) != Py_None;
+#else
+    // FIXME: Make this the default code path once Limited API has been raised to 3.13
+    // Note: PyWeakref_GetObject() will be removed in 3.15.
+    PyObject *pobj = nullptr;
+    const bool result = PyWeakref_GetRef(weakRef, &pobj) == 1;
+    Py_XDECREF(pobj);
+    return result;
+#endif
+}
index e1bf773b310f8d6d80a072a9af694bd50d79fe9d..d3d644649939ef12fc3ca6dd3457177a39ecc988 100644 (file)
@@ -4,6 +4,8 @@
 #ifndef PEP384IMPL_H
 #define PEP384IMPL_H
 
+#include "shibokenmacros.h"
+
 extern "C"
 {
 
@@ -191,8 +193,12 @@ LIBSHIBOKEN_API int Pep_GetVerboseFlag(void);
 // pyerrors.h
 #if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000
 LIBSHIBOKEN_API PyObject *PepErr_GetRaisedException();
+LIBSHIBOKEN_API PyObject *PepException_GetArgs(PyObject *ex);
+LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args);
 #else
 #  define PepErr_GetRaisedException PyErr_GetRaisedException
+#  define PepException_GetArgs PyException_GetArgs
+#  define PepException_SetArgs PyException_SetArgs
 #endif
 
 /*****************************************************************************
@@ -219,9 +225,6 @@ LIBSHIBOKEN_API PyObject *PepErr_GetRaisedException();
 // needed a debug Python.
 //
 
-// PyUnicode_GetSize is deprecated in favor of PyUnicode_GetLength.
-#define PepUnicode_GetLength(op)    PyUnicode_GetLength((PyObject *)(op))
-
 // Unfortunately, we cannot ask this at runtime
 // #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
 // FIXME: Python 3.10: Replace _PepUnicode_AsString by PyUnicode_AsUTF8
@@ -578,8 +581,6 @@ LIBSHIBOKEN_API PyTypeObject *PepType_Type_tp_new(PyTypeObject *metatype,
 #define Py_TPFLAGS_METHOD_DESCRIPTOR (1UL << 17)
 #endif
 
-extern LIBSHIBOKEN_API int PepRuntime_38_flag;
-
 /*****************************************************************************
  *
  * Runtime support for Python 3.12 incompatibility
index bcc3fb767725225e67ba2a5e78c8c8befec515d6..886baf1bbd5b7571c295d7104a6bd8850f1635c4 100644 (file)
@@ -22,9 +22,8 @@ static bool sequenceAllOf(PyObject *pyIn, Predicate p)
 {
     const Py_ssize_t size = PySequence_Size(pyIn);
     for (Py_ssize_t i = 0; i < size; ++i) {
-        PyObject *item = PySequence_GetItem(pyIn, i);
+        AutoDecRef item(PySequence_GetItem(pyIn, i));
         const bool ok = p(item);
-        Py_XDECREF(item);
         if (!ok)
             return false;
     }
@@ -37,9 +36,8 @@ inline void convertPySequence(PyObject *pyIn, Converter c, T *out)
 {
     const Py_ssize_t size = PySequence_Size(pyIn);
     for (Py_ssize_t i = 0; i < size; ++i) {
-        PyObject *item = PySequence_GetItem(pyIn, i);
+        AutoDecRef item(PySequence_GetItem(pyIn, i));
         *out++ = c(item);
-        Py_XDECREF(item);
     }
 }
 
@@ -81,7 +79,7 @@ static void sequenceToCppShortArray(PyObject *pyIn, void *cppOut)
 static inline bool sequenceSizeCheck(PyObject *pyIn, int expectedSize = -1)
 {
     if (expectedSize >= 0) {
-        const int size = int(PySequence_Size(pyIn));
+        const Py_ssize_t size = PySequence_Size(pyIn);
         if (size < expectedSize) {
             warning(PyExc_RuntimeWarning, 0, "A sequence of size %d was passed to a function that expects %d.",
                    size, expectedSize);
index f07cb1d70e3b1353b505dd1733a2b0de78ec5c59..dcb9bfb3832e8fbed5182321731ee20907008b8b 100644 (file)
@@ -70,7 +70,7 @@ template <class T, int columns>
 class Array2Handle
 {
 public:
-    typedef T RowType[columns];
+    using RowType = T[columns];
 
     Array2Handle() = default;
 
index 09a21ed136292ee9e7b98cfcbb5f7d9850aca5d2..97d7a3628a83eb2ab862413cbe34917e680356dc 100644 (file)
 #include "voidptr.h"
 
 #include <string>
+#include <cstring>
+#include <iostream>
 #include <unordered_map>
 #include <unordered_set>
+#include <map>
+#include <set>
 
 static SbkConverter **PrimitiveTypeConverters;
 
@@ -73,13 +77,110 @@ void init()
     initArrayConverters();
 }
 
+static void dumpPyTypeObject(std::ostream &str, PyTypeObject *t)
+{
+    str << "\nPython type ";
+    if (t == nullptr) {
+        str << "<None>";
+        return;
+    }
+    str << '"' << t->tp_name << '"';
+    if (t->tp_base != nullptr && t->tp_base != &PyBaseObject_Type)
+        str << '(' << t->tp_base->tp_name << ')';
+}
+
+static void dumpSbkConverter(std::ostream &str, const SbkConverter *c)
+{
+    str << "SbkConverter " << static_cast<const void *>(c) << ": ";
+    if (c->pointerToPython != nullptr)
+        str << ", C++ pointer->Python";
+    if (c->copyToPython != nullptr)
+        str << ", copy->Python";
+    if (c->toCppPointerConversion.second != nullptr)
+        str << ", Python->C++ pointer";
+    if (!c->toCppConversions.empty())
+        str << ", " << c->toCppConversions.size() << " Python->C++ conversions";
+}
+
+// Less than operator for a PyTypeObject for dumping the converter map
+static bool pyTypeObjectLessThan(const PyTypeObject *t1, const PyTypeObject *t2)
+{
+    const bool isNull1 = t1 == nullptr;
+    const bool isNull2 = t2 == nullptr;
+    if (isNull1 || isNull2)
+        return isNull1 && !isNull2;
+    // Internal types (lower case) first
+    const bool isInternal1 = std::islower(t1->tp_name[0]);
+    const bool isInternal2 = std::islower(t2->tp_name[0]);
+    if (isInternal1 != isInternal2)
+        return !isInternal2;
+    return std::strcmp(t1->tp_name, t2->tp_name) < 0;
+}
+
+void dumpConverters()
+{
+    struct PyTypeObjectLess {
+
+        bool operator()(const PyTypeObject *t1, const PyTypeObject *t2) const {
+            return pyTypeObjectLessThan(t1, t2);
+        }
+    };
+
+    using StringSet = std::set<std::string>;
+    using SbkConverterNamesMap = std::unordered_map<SbkConverter *, StringSet>;
+    using PyTypeObjectConverterMap = std::map<PyTypeObject *, SbkConverterNamesMap,
+                                              PyTypeObjectLess>;
+
+    auto &str = std::cerr;
+
+    // Sort the entries by the associated PyTypeObjects and converters
+    PyTypeObjectConverterMap pyTypeObjectConverterMap;
+    for (const auto &converter : converters) {
+        auto *sbkConverter = converter.second;
+        if (sbkConverter == nullptr) {
+            str << "Non-existent: \"" << converter.first << "\"\n";
+            continue;
+        }
+        auto *typeObject = sbkConverter->pythonType;
+        auto typeIt = pyTypeObjectConverterMap.find(typeObject);
+        if (typeIt == pyTypeObjectConverterMap.end())
+            typeIt = pyTypeObjectConverterMap.insert(std::make_pair(typeObject,
+                                                                    SbkConverterNamesMap{})).first;
+        SbkConverterNamesMap &sbkConverterMap = typeIt->second;
+        auto convIt = sbkConverterMap.find(sbkConverter);
+        if (convIt == sbkConverterMap.end())
+            convIt = sbkConverterMap.insert(std::make_pair(sbkConverter,
+                                                           StringSet{})).first;
+        convIt->second.insert(converter.first);
+    }
+
+     for (const auto &tc : pyTypeObjectConverterMap) {
+         dumpPyTypeObject(str, tc.first);
+         str << ", " << tc.second.size() << " converter(s):\n";
+         for (const auto &cn : tc.second) {
+             str << "    ";
+             dumpSbkConverter(str, cn.first);
+             str << ", " << cn.second.size() << " alias(es):";
+             int i = 0;
+             for (const auto &name : cn.second) {
+                 if ((i++ % 5) == 0)
+                     str << "\n        ";
+                 str << " \"" << name << '"';
+             }
+             str << '\n';
+         }
+    }
+
+    str << '\n';
+}
+
 SbkConverter *createConverterObject(PyTypeObject *type,
                                            PythonToCppFunc toCppPointerConvFunc,
                                            IsConvertibleToCppFunc toCppPointerCheckFunc,
                                            CppToPythonFunc pointerToPythonFunc,
                                            CppToPythonFunc copyToPythonFunc)
 {
-    auto converter = new SbkConverter;
+    auto *converter = new SbkConverter;
     converter->pythonType = type;
     // PYSIDE-595: All types are heaptypes now, so provide reference.
     Py_XINCREF(type);
@@ -140,6 +241,14 @@ void addPythonToCppValueConversion(SbkConverter *converter,
     converter->toCppConversions.push_back(std::make_pair(isConvertibleToCppFunc, pythonToCppFunc));
 }
 
+void prependPythonToCppValueConversion(SbkConverter *converter,
+                                       PythonToCppFunc pythonToCppFunc,
+                                       IsConvertibleToCppFunc isConvertibleToCppFunc)
+{
+    converter->toCppConversions.insert(converter->toCppConversions.begin(),
+                                       std::make_pair(isConvertibleToCppFunc, pythonToCppFunc));
+}
+
 void addPythonToCppValueConversion(PyTypeObject *type,
                                    PythonToCppFunc pythonToCppFunc,
                                    IsConvertibleToCppFunc isConvertibleToCppFunc)
@@ -417,6 +526,15 @@ bool isImplicitConversion(PyTypeObject *type, PythonToCppFunc toCppFunc)
 }
 
 void registerConverterName(SbkConverter *converter, const char *typeName)
+{
+    auto iter = converters.find(typeName);
+    if (iter == converters.end())
+        converters.insert(std::make_pair(typeName, converter));
+    else
+        iter->second = converter;
+}
+
+void registerConverterAlias(SbkConverter *converter, const char *typeName)
 {
     auto iter = converters.find(typeName);
     if (iter == converters.end())
index 0d68f3fafa1396ae06e5fbeb205f78b315b8c0fe..22d0f923f454ae22ee4b7fa00967be6b570a1d59 100644 (file)
@@ -144,6 +144,10 @@ LIBSHIBOKEN_API void setPythonToCppPointerFunctions(SbkConverter *converter,
 LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkConverter *converter,
                                                    PythonToCppFunc pythonToCppFunc,
                                                    IsConvertibleToCppFunc isConvertibleToCppFunc);
+LIBSHIBOKEN_API void prependPythonToCppValueConversion(SbkConverter *converter,
+                                                       PythonToCppFunc pythonToCppFunc,
+                                                       IsConvertibleToCppFunc isConvertibleToCppFunc);
+
 LIBSHIBOKEN_API void addPythonToCppValueConversion(PyTypeObject *type,
                                                    PythonToCppFunc pythonToCppFunc,
                                                    IsConvertibleToCppFunc isConvertibleToCppFunc);
@@ -266,8 +270,14 @@ LIBSHIBOKEN_API void nonePythonToCppNullPtr(PyObject *, void *cppOut);
 [[deprecated]] LIBSHIBOKEN_API bool isImplicitConversion(PyTypeObject *type, PythonToCppFunc toCpp);
 
 /// Registers a converter with a type name that may be used to retrieve the converter.
+/// Use for fully qualified names (main type). This will overwrite existing converters
+/// of the same name.
 LIBSHIBOKEN_API void registerConverterName(SbkConverter *converter, const char *typeName);
 
+/// Registers a converter with a type name that may be used to retrieve the converter
+/// unless there is already a converter for the name. Use for partially qualified names.
+LIBSHIBOKEN_API void registerConverterAlias(SbkConverter *converter, const char *typeName);
+
 /// Returns the converter for a given type name, or NULL if it wasn't registered before.
 LIBSHIBOKEN_API SbkConverter *getConverter(const char *typeName);
 
@@ -339,27 +349,29 @@ LIBSHIBOKEN_API bool pythonTypeIsObjectType(const SbkConverter *converter);
 /// Returns true if the Python type associated with the converter is a wrapper type.
 LIBSHIBOKEN_API bool pythonTypeIsWrapperType(const SbkConverter *converter);
 
-#define SBK_PY_LONG_LONG_IDX            0
+enum  : int {
+SBK_PY_LONG_LONG_IDX =             0,
 // Qt5: name collision in QtCore after QBool is replaced by bool
-#define SBK_BOOL_IDX_1                  1
-#define SBK_CHAR_IDX                    2
-#define SBK_CONSTCHARPTR_IDX            3
-#define SBK_DOUBLE_IDX                  4
-#define SBK_FLOAT_IDX                   5
-#define SBK_INT_IDX                     6
-#define SBK_SIGNEDINT_IDX               6
-#define SBK_LONG_IDX                    7
-#define SBK_SHORT_IDX                   8
-#define SBK_SIGNEDCHAR_IDX              9
-#define SBK_STD_STRING_IDX             10
-#define SBK_STD_WSTRING_IDX            11
-#define SBK_UNSIGNEDPY_LONG_LONG_IDX   12
-#define SBK_UNSIGNEDCHAR_IDX           13
-#define SBK_UNSIGNEDINT_IDX            14
-#define SBK_UNSIGNEDLONG_IDX           15
-#define SBK_UNSIGNEDSHORT_IDX          16
-#define SBK_VOIDPTR_IDX                17
-#define SBK_NULLPTR_T_IDX              18
+SBK_BOOL_IDX_1 =                   1,
+SBK_CHAR_IDX =                     2,
+SBK_CONSTCHARPTR_IDX =             3,
+SBK_DOUBLE_IDX =                   4,
+SBK_FLOAT_IDX =                    5,
+SBK_INT_IDX =                      6,
+SBK_SIGNEDINT_IDX =                6,
+SBK_LONG_IDX =                     7,
+SBK_SHORT_IDX =                    8,
+SBK_SIGNEDCHAR_IDX =               9,
+SBK_STD_STRING_IDX =              10,
+SBK_STD_WSTRING_IDX =             11,
+SBK_UNSIGNEDPY_LONG_LONG_IDX =    12,
+SBK_UNSIGNEDCHAR_IDX =            13,
+SBK_UNSIGNEDINT_IDX =             14,
+SBK_UNSIGNEDLONG_IDX =            15,
+SBK_UNSIGNEDSHORT_IDX =           16,
+SBK_VOIDPTR_IDX =                 17,
+SBK_NULLPTR_T_IDX =               18
+};
 
 template<typename T> SbkConverter *PrimitiveTypeConverter() { return nullptr; }
 template<> inline SbkConverter *PrimitiveTypeConverter<PY_LONG_LONG>() { return primitiveTypeConverter(SBK_PY_LONG_LONG_IDX); }
index 9b2d5dbabb00e02350dbb179a9893a5323779d6f..b4ef51d44ef586f544d3ef923d002bc7fd46e76c 100644 (file)
@@ -221,7 +221,7 @@ struct IntPrimitive : TwoPrimitive<INT>
     }
     static void toCpp(PyObject *pyIn, void *cppOut)
     {
-        double result = PyFloat_AS_DOUBLE(pyIn);
+        double result = PyFloat_AsDouble(pyIn);
         // If cast to long directly it could overflow silently.
         if (OverFlowChecker<INT>::check(result, pyIn))
             PyErr_SetObject(PyExc_OverflowError, nullptr);
@@ -532,6 +532,8 @@ SbkConverter *createConverterObject(PyTypeObject *type,
                                     CppToPythonFunc pointerToPythonFunc,
                                     CppToPythonFunc copyToPythonFunc);
 
+LIBSHIBOKEN_API void dumpConverters();
+
 /// Interface for sbkmodule which must reset cache when new module is loaded.
 LIBSHIBOKEN_API void clearNegativeLazyCache();
 
index 8e8324f5ef50d40c9c7aeda3475f5d93b479753f..1aec93f17f247e63bdcaf32c8513fcce20c13269 100644 (file)
@@ -36,7 +36,7 @@ void toCppString(PyObject *str, std::string *value)
     }
 
     if (PyBytes_Check(str))
-        value->assign(PyBytes_AS_STRING(str));
+        value->assign(PyBytes_AsString(str));
 }
 
 void toCppWString(PyObject *str, std::wstring *value)
index 4c0597bda4fa21359f3829da7ec1cf4de730d91c..787d4ac0db02e43de8599043e93c32b2c3fd1e45 100644 (file)
@@ -9,8 +9,7 @@
 #include "sbkconverter.h"
 #include "basewrapper.h"
 #include "autodecref.h"
-#include "sbkpython.h"
-#include "signature.h"
+#include "sbktypefactory.h"
 
 #include <cstring>
 #include <vector>
@@ -103,9 +102,9 @@ int enumIsFlag(PyObject *ob_type)
     if (metatype != reinterpret_cast<PyTypeObject *>(PyEnumMeta))
         return -1;
     auto *mro = reinterpret_cast<PyTypeObject *>(ob_type)->tp_mro;
-    const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+    const Py_ssize_t n = PyTuple_Size(mro);
     for (Py_ssize_t idx = 0; idx < n; ++idx) {
-        auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+        auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
         if (sub_type == reinterpret_cast<PyTypeObject *>(PyFlag))
             return 1;
     }
@@ -143,7 +142,8 @@ static PyObject *missing_func(PyObject * /* self */ , PyObject *args)
     static auto *const _mro = Shiboken::String::createStaticString("__mro__");
     static auto *const _class = Shiboken::String::createStaticString("__class__");
 
-    PyObject *klass{}, *value{};
+    PyObject *klass{};
+    PyObject *value{};
     if (!PyArg_UnpackTuple(args, "missing", 2, 2, &klass, &value))
         Py_RETURN_NONE;
     if (!PyLong_Check(value))
@@ -217,11 +217,16 @@ namespace Shiboken::Enum {
 int enumOption{};
 
 bool check(PyObject *pyObj)
+{
+    return checkType(Py_TYPE(pyObj));
+}
+
+bool checkType(PyTypeObject *pyTypeObj)
 {
     init_enum();
 
     static PyTypeObject *meta = getPyEnumMeta();
-    return Py_TYPE(Py_TYPE(pyObj)) == reinterpret_cast<PyTypeObject *>(meta);
+    return Py_TYPE(pyTypeObj) == reinterpret_cast<PyTypeObject *>(meta);
 }
 
 PyObject *getEnumItemFromValue(PyTypeObject *enumType, EnumValueType itemValue)
@@ -269,10 +274,12 @@ EnumValueType getValue(PyObject *enumItem)
     return PyLong_AsLongLong(pyValue);
 }
 
-void setTypeConverter(PyTypeObject *type, SbkConverter *converter)
+void setTypeConverter(PyTypeObject *type, SbkConverter *converter,
+                      SbkConverter *flagsConverter)
 {
-    auto *enumType = reinterpret_cast<SbkEnumType *>(type);
-    PepType_SETP(enumType)->converter = converter;
+    SbkEnumTypePrivate *priv = PepType_SETP(reinterpret_cast<SbkEnumType *>(type));
+    priv->converter = converter;
+    priv->flagsConverter = flagsConverter;
 }
 
 static PyTypeObject *createEnumForPython(PyObject *scopeOrModule,
@@ -394,8 +401,8 @@ static PyTypeObject *createPythonEnumHelper(PyObject *module,
         auto *key = PyUnicode_FromString(kv);
         auto *value = toPyObject(enumValues[idx]);
         auto *key_value = PyTuple_New(2);
-        PyTuple_SET_ITEM(key_value, 0, key);
-        PyTuple_SET_ITEM(key_value, 1, value);
+        PyTuple_SetItem(key_value, 0, key);
+        PyTuple_SetItem(key_value, 1, value);
         PyList_Append(pyEnumItems, key_value);
     }
     return createEnumForPython(module, fullName, pyEnumItems);
index e19ca4b4c76cb779c5717be138e6407842560936..55503b5ec89879ca31547d79540b4abd59f7767f 100644 (file)
@@ -21,6 +21,7 @@ struct SbkEnumType;
 struct SbkEnumTypePrivate
 {
     SbkConverter *converter;
+    SbkConverter *flagsConverter;
 };
 
 /// PYSIDE-1735: Pass on the Python enum/flag information.
@@ -52,6 +53,7 @@ LIBSHIBOKEN_API extern int enumOption;
 using EnumValueType = long long;
 
 LIBSHIBOKEN_API bool check(PyObject *obj);
+LIBSHIBOKEN_API bool checkType(PyTypeObject *pyTypeObj);
 
 LIBSHIBOKEN_API PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue,
                                   const char *itemName = nullptr);
@@ -61,7 +63,8 @@ LIBSHIBOKEN_API PyObject *getEnumItemFromValue(PyTypeObject *enumType,
                                                EnumValueType itemValue);
 
 /// Sets the enum/flag's type converter.
-LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter);
+LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter,
+                                      SbkConverter *flagsConverter = nullptr);
 
 /// Creating Python enums for different types.
 LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module,
index 1832624d5dbc26966fa77215ce3449e7e86ddea5..39f67ddeb07af7285db75ea63d2ba56dbb505e4c 100644 (file)
@@ -6,6 +6,11 @@
 #include "helper.h"
 #include "gilstate.h"
 
+#include <cstdio>
+#include <string>
+
+using namespace std::literals::string_literals;
+
 namespace Shiboken
 {
 
@@ -36,6 +41,12 @@ void PythonContextMarker::setBlocking()
 namespace Errors
 {
 
+void setIndexOutOfBounds(Py_ssize_t value, Py_ssize_t minValue, Py_ssize_t maxValue)
+{
+    PyErr_Format(PyExc_IndexError,
+                 "index %zd out of bounds %zd..%zd", value, minValue, maxValue);
+}
+
 void setInstantiateAbstractClass(const char *name)
 {
     PyErr_Format(PyExc_NotImplementedError,
@@ -93,6 +104,21 @@ void setWrongContainerType()
     PyErr_SetString(PyExc_TypeError, "Wrong type passed to container conversion.");
 }
 
+// Prepend something to an exception message provided it is a single string
+// argument.
+static bool prependToExceptionMessage(PyObject *exc, const char *context)
+{
+    Shiboken::AutoDecRef args(PepException_GetArgs(exc));
+    if (args.isNull() || PyTuple_Check(args.object()) == 0 || PyTuple_Size(args) != 1)
+        return false;
+    auto *oldMessage = PyTuple_GetItem(args, 0);
+    if (oldMessage == nullptr || PyUnicode_CheckExact(oldMessage) == 0)
+        return false;
+    auto *newMessage = PyUnicode_FromFormat("%s%U", context, oldMessage);
+    PepException_SetArgs(exc, PyTuple_Pack(1, newMessage));
+    return true;
+}
+
 struct ErrorStore {
     PyObject *type;
     PyObject *exc;
@@ -101,17 +127,42 @@ struct ErrorStore {
 
 static thread_local ErrorStore savedError{};
 
+static bool hasPythonContext()
+{
+    return _pythonContextStack & 1;
+}
+
 void storeErrorOrPrint()
 {
     // This error happened in a function with no way to return an error state.
     // Therefore, we handle the error when we are error checking, anyway.
     // But we do that only when we know that an error handler can pick it up.
-    if (_pythonContextStack & 1)
+    if (hasPythonContext())
         PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback);
     else
         PyErr_Print();
 }
 
+// Like storeErrorOrPrint() with additional context info that is prepended
+// to the exception message or printed.
+static void storeErrorOrPrintWithContext(const char *context)
+{
+    if (hasPythonContext()) {
+        PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback);
+        prependToExceptionMessage(savedError.exc, context);
+    } else {
+        std::fputs(context, stderr);
+        PyErr_Print();
+    }
+}
+
+void storePythonOverrideErrorOrPrint(const char *className, const char *funcName)
+{
+    const std::string context = "Error calling Python override of "s
+                                + className + "::"s + funcName + "(): "s;
+    storeErrorOrPrintWithContext(context.c_str());
+}
+
 PyObject *occurred()
 {
     if (savedError.type) {
index 6ff85f8e1f0a8ffdff83e33570697c0f61bd3aec..2926cbc654951ec1d89f30763e58c6a3b41c2192 100644 (file)
@@ -35,6 +35,8 @@ public:
 namespace Errors
 {
 
+LIBSHIBOKEN_API void setIndexOutOfBounds(Py_ssize_t value, Py_ssize_t minValue,
+                                         Py_ssize_t maxValue);
 LIBSHIBOKEN_API void setInstantiateAbstractClass(const char *name);
 LIBSHIBOKEN_API void setInstantiateAbstractClassDisabledWrapper(const char *name);
 LIBSHIBOKEN_API void setInvalidTypeDeletion(const char *name);
@@ -50,6 +52,11 @@ LIBSHIBOKEN_API void setWrongContainerType();
 /// This replaces `PyErr_Print`, which cannot report errors as exception.
 /// To be used in contexts where raising errors is impossible.
 LIBSHIBOKEN_API void storeErrorOrPrint();
+
+/// Call storeErrorOrPrint() and print the context to report
+/// errors when calling Python overrides of virtual functions.
+LIBSHIBOKEN_API void storePythonOverrideErrorOrPrint(const char *className, const char *funcName);
+
 /// Handle an error as in PyErr_Occurred(), but also check for errors which
 /// were captured by `storeErrorOrPrint`.
 /// To be used in normal error checks.
index f31b8f4f7f430dfecbee26fdadd18c8207a8958b..fe32d8ca2bece8fa7ac7c5debc9f84d184ad9d29 100644 (file)
@@ -92,8 +92,12 @@ void disassembleFrame(const char *marker)
     PyErr_Restore(error_type, error_value, error_traceback);
 }
 
-// python 3.12
-static int const CALL = 171;
+// Python 3.13
+static int const LOAD_ATTR_313 = 82;
+static int const CALL_313 = 53;
+static int const PUSH_NULL_313 = 34;
+// Python 3.12
+static int const CALL_312 = 171;
 // Python 3.11
 static int const PRECALL = 166;
 // we have "big instructions" with gaps after them
@@ -105,13 +109,16 @@ static int const LOAD_METHOD = 160;
 static int const CALL_METHOD = 161;
 // Python 3.6
 static int const CALL_FUNCTION = 131;
-static int const LOAD_ATTR = 106;
+static int const LOAD_ATTR_312 = 106;
 // NoGil (how long will this exist in this form?)
 static int const LOAD_METHOD_NOGIL = 55;
 static int const CALL_METHOD_NOGIL = 72;
 
 static bool currentOpcode_Is_CallMethNoArgs()
 {
+    static auto number = _PepRuntimeVersion();
+    static int LOAD_ATTR = number < 0x030D00 ? LOAD_ATTR_312 : LOAD_ATTR_313;
+    static int CALL = number < 0x030D00 ? CALL_312 : CALL_313;
     // PYSIDE-2221: Special case for the NoGil version:
     //              Find out if we have such a version.
     //              We could also ask the variable `Py_NOGIL`.
@@ -120,6 +127,8 @@ static bool currentOpcode_Is_CallMethNoArgs()
     // We look into the currently active operation if we are going to call
     // a method with zero arguments.
     auto *frame = PyEval_GetFrame();
+    if (frame == nullptr) // PYSIDE-2796, has been observed to fail
+        return false;
 #if !Py_LIMITED_API && !defined(PYPY_VERSION)
     auto *f_code = PyFrame_GetCode(frame);
 #else
@@ -148,7 +157,6 @@ static bool currentOpcode_Is_CallMethNoArgs()
     }
     uint8_t opcode2 = co_code[f_lasti + 2];
     uint8_t oparg2 = co_code[f_lasti + 3];
-    static auto number = _PepRuntimeVersion();
     if (number < 0x030B00)
         return opcode1 == LOAD_METHOD && opcode2 == CALL_METHOD && oparg2 == 0;
 
@@ -158,7 +166,7 @@ static bool currentOpcode_Is_CallMethNoArgs()
         //       don't need to take care of them.
         if (opcode1 == LOAD_METHOD)
             f_lasti += LOAD_METHOD_GAP_311;
-        else if (opcode1 == LOAD_ATTR)
+        else if (opcode1 == LOAD_ATTR_312)
             f_lasti += LOAD_ATTR_GAP_311;
         else
             return false;
@@ -176,6 +184,11 @@ static bool currentOpcode_Is_CallMethNoArgs()
     else
         return false;
 
+    if (number >= 0x030D00) {
+        int opcode3 = co_code[f_lasti + 2];
+        if (opcode3 == PUSH_NULL_313)
+            f_lasti += 2;
+    }
     opcode2 = co_code[f_lasti + 2];
     oparg2 = co_code[f_lasti + 3];
 
@@ -230,9 +243,8 @@ static PyObject *lookupUnqualifiedOrOldEnum(PyTypeObject *type, PyObject *name)
     PyObject *mro = type->tp_mro;
     PyObject *result{};
     assert(PyTuple_Check(mro));
-    Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
-    for (idx = 0; idx < n; ++idx) {
-        auto *base = PyTuple_GET_ITEM(mro, idx);
+    for (Py_ssize_t idx = 0, n = PyTuple_Size(mro); idx < n; ++idx) {
+        auto *base = PyTuple_GetItem(mro, idx);
         auto *type_base = reinterpret_cast<PyTypeObject *>(base);
         if (!SbkObjectType_Check(type_base))
             continue;
index adcd675616622a6ecc45138eb2e6d51fee9baf82..f254cdfd14e4806c4b431dabb8500f2e4768c597 100644 (file)
 
 #include <unordered_map>
 #include <unordered_set>
+#include <vector>
 #include <cstring>
 
+/// This hash maps module objects to arrays of converters.
+using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **> ;
+
 /// This hash maps module objects to arrays of Python types.
 using ModuleTypesMap = std::unordered_map<PyObject *, Shiboken::Module::TypeInitStruct *> ;
 
-/// This hash maps module objects to arrays of converters.
-using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **>;
+struct TypeCreationStruct
+{
+    Shiboken::Module::TypeCreationFunction func;
+    std::vector<std::string> subtypeNames;
+};
 
-/// This hash maps type names to type creation functions.
-using TypeCreationFunctionModulePair =
-    std::pair<Shiboken::Module::TypeCreationFunction, PyObject *>;
-using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationFunctionModulePair>;
+/// This hash maps type names to type creation structs.
+using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationStruct> ;
 
 /// This hash maps module objects to maps of names to functions.
 using ModuleToFuncsMap = std::unordered_map<PyObject *, NameToTypeFunctionMap> ;
@@ -75,6 +80,41 @@ LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct)
     return typeStruct.type;
 }
 
+static void incarnateHelper(PyObject *module, const std::string_view names,
+                            const NameToTypeFunctionMap &nameToFunc)
+{
+    auto dotPos = names.find('.');
+    std::string::size_type startPos = 0;
+    auto *modOrType{module};
+    while (dotPos != std::string::npos) {
+        auto typeName = names.substr(startPos, dotPos - startPos);
+        AutoDecRef obTypeName(String::fromCppStringView(typeName));
+        modOrType = PyObject_GetAttr(modOrType, obTypeName);
+        startPos = dotPos + 1;
+        dotPos = names.find('.', startPos);
+    }
+    // now we have the type to create. (May be done already)
+    auto funcIter = nameToFunc.find(std::string(names));
+    if (funcIter == nameToFunc.end())
+        return;
+    // - call this function that returns a PyTypeObject
+    auto tcStruct = funcIter->second;
+    auto initFunc = tcStruct.func;
+    PyTypeObject *type = initFunc(modOrType);
+    auto name = names.substr(startPos);
+    PyObject_SetAttrString(modOrType, name.data(), reinterpret_cast<PyObject *>(type));
+}
+
+static void incarnateSubtypes(PyObject *module,
+                              const std::vector<std::string> &nameList,
+                              NameToTypeFunctionMap &nameToFunc)
+{
+    for (auto const & tableIter : nameList) {
+        std::string_view names(tableIter);
+        incarnateHelper(module, names, nameToFunc);
+    }
+}
+
 static PyTypeObject *incarnateType(PyObject *module, const char *name,
                                    NameToTypeFunctionMap &nameToFunc)
 {
@@ -86,13 +126,15 @@ static PyTypeObject *incarnateType(PyObject *module, const char *name,
         return nullptr;
     }
     // - call this function that returns a PyTypeObject
-    auto pair = funcIter->second;
-    auto initFunc = pair.first;
-    auto *modOrType = pair.second;
+    auto tcStruct = funcIter->second;
+    auto initFunc = tcStruct.func;
+    auto *modOrType{module};
 
     // PYSIDE-2404: Make sure that no switching happens during type creation.
     auto saveFeature = initSelectableFeature(nullptr);
     PyTypeObject *type = initFunc(modOrType);
+    if (!tcStruct.subtypeNames.empty())
+        incarnateSubtypes(module, tcStruct.subtypeNames, nameToFunc);
     initSelectableFeature(saveFeature);
 
     // - assign this object to the name in the module
@@ -124,6 +166,7 @@ void loadLazyClassesWithName(const char *name)
 
 // PYSIDE-2404: Completely load all not yet loaded classes.
 //              This is needed to resolve a star import.
+// PYSIDE-2898: Use a name list to pick the toplevel types.
 void resolveLazyClasses(PyObject *module)
 {
     // - locate the module in the moduleTofuncs mapping
@@ -134,11 +177,19 @@ void resolveLazyClasses(PyObject *module)
     // - see if there are still unloaded elements
     auto &nameToFunc = tableIter->second;
 
-    // - incarnate all types.
-    while (!nameToFunc.empty()) {
-        auto it = nameToFunc.begin();
-        auto attrNameStr = it->first;
-        incarnateType(module, attrNameStr.c_str(), nameToFunc);
+    // - keep a filtered list of names without the subtypes
+    std::vector<std::string> names{};
+    names.reserve(nameToFunc.size());
+    for (const auto &funcIter : nameToFunc) {
+        if (funcIter.first.find('.') == std::string::npos)
+            names.push_back(funcIter.first);
+    }
+
+    // - incarnate all toplevel types. Subtypes are handled there.
+    for (const auto &nameIter : names) {
+        auto funcIter = nameToFunc.find(nameIter);
+        if (funcIter != nameToFunc.end())
+            incarnateType(module, nameIter.c_str(), nameToFunc);
     }
 }
 
@@ -152,7 +203,7 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name)
     // - check if the attribute is present and return it.
     auto *attr = PyObject_GenericGetAttr(module, name);
     // - we handle AttributeError, only.
-    if (!(attr == nullptr && PyErr_ExceptionMatches(PyExc_AttributeError)))
+    if (attr != nullptr || PyErr_ExceptionMatches(PyExc_AttributeError) == 0)
         return attr;
 
     PyErr_Clear();
@@ -165,7 +216,7 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name)
     // - locate the name and retrieve the generating function
     const char *attrNameStr = Shiboken::String::toCString(name);
     auto &nameToFunc = tableIter->second;
-    // - create the real type (incarnateType checks this)
+    // - create the real type and handle subtypes
     auto *type = incarnateType(module, attrNameStr, nameToFunc);
     auto *ret = reinterpret_cast<PyObject *>(type);
     // - if attribute does really not exist use the original
@@ -173,7 +224,6 @@ static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name)
         PyErr_Clear();
         return origModuleGetattro(module, name);
     }
-
     return ret;
 }
 
@@ -209,6 +259,9 @@ static PyMethodDef module_methods[] = {
 // Python 3.8 - 3.12
 static int const LOAD_CONST_312 = 100;
 static int const IMPORT_NAME_312 = 108;
+// Python 3.13
+static int const LOAD_CONST_313 = 83;
+static int const IMPORT_NAME_313 = 75;
 
 static bool isImportStar(PyObject *module)
 {
@@ -221,6 +274,9 @@ static bool isImportStar(PyObject *module)
     static PyObject *const _co_consts = Shiboken::String::createStaticString("co_consts");
     static PyObject *const _co_names = Shiboken::String::createStaticString("co_names");
 
+    static int LOAD_CONST = _PepRuntimeVersion() < 0x030D00 ? LOAD_CONST_312 : LOAD_CONST_313;
+    static int IMPORT_NAME = _PepRuntimeVersion() < 0x030D00 ? IMPORT_NAME_312 : IMPORT_NAME_313;
+
     auto *obFrame = reinterpret_cast<PyObject *>(PyEval_GetFrame());
     if (obFrame == nullptr)
         return true;            // better assume worst-case.
@@ -235,12 +291,12 @@ static bool isImportStar(PyObject *module)
         AutoDecRef dec_co_code(PyObject_GetAttr(dec_f_code, _co_code));
         AutoDecRef dec_f_lasti(PyObject_GetAttr(dec_frame, _f_lasti));
         Py_ssize_t f_lasti = PyLong_AsSsize_t(dec_f_lasti);
-        Py_ssize_t code_len;
+        Py_ssize_t code_len{};
         char *co_code{};
         PyBytes_AsStringAndSize(dec_co_code, &co_code, &code_len);
         uint8_t opcode2 = co_code[f_lasti];
         uint8_t opcode1 = co_code[f_lasti - 2];
-        if (opcode1 == LOAD_CONST_312 && opcode2 == IMPORT_NAME_312) {
+        if (opcode1 == LOAD_CONST && opcode2 == IMPORT_NAME) {
             uint8_t oparg1 = co_code[f_lasti - 1];
             uint8_t oparg2 = co_code[f_lasti + 1];
             AutoDecRef dec_co_consts(PyObject_GetAttr(dec_f_code, _co_consts));
@@ -261,8 +317,6 @@ static bool isImportStar(PyObject *module)
 
 // PYSIDE-2404: These modules produce ambiguous names which we cannot handle, yet.
 static std::unordered_set<std::string> dontLazyLoad{
-    "sample",
-    "smart",
     "testbinding"
 };
 
@@ -305,24 +359,11 @@ static int lazyLoadDefault()
     return result;
 }
 
-void AddTypeCreationFunction(PyObject *module,
-                             const char *name,
-                             TypeCreationFunction func)
+void checkIfShouldLoadImmediately(PyObject *module, const std::string &name,
+                                  const NameToTypeFunctionMap &nameToFunc)
 {
     static const int value = lazyLoadDefault();
 
-    // - locate the module in the moduleTofuncs mapping
-    auto tableIter = moduleToFuncs.find(module);
-    assert(tableIter != moduleToFuncs.end());
-    // - Assign the name/generating function pair.
-    auto &nameToFunc = tableIter->second;
-    TypeCreationFunctionModulePair pair{func, module};
-    auto nit = nameToFunc.find(name);
-    if (nit == nameToFunc.end())
-        nameToFunc.insert(std::make_pair(name, pair));
-    else
-        nit->second = pair;
-
     // PYSIDE-2404: Lazy Loading
     //
     // Options:
@@ -331,56 +372,56 @@ void AddTypeCreationFunction(PyObject *module,
     //   3  - lazy loading for any module.
     //
     // By default we lazy load all known modules (option = 1).
-
     if (value == 0                                  // completely disabled
         || canNotLazyLoad(module)                   // for some reason we cannot lazy load
         || (value == 1 && !shouldLazyLoad(module))  // not a known module
         ) {
-        PyTypeObject *type = func(module);
-        PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(type));   // steals reference
+        incarnateHelper(module, name, nameToFunc);
     }
 }
 
 void AddTypeCreationFunction(PyObject *module,
                              const char *name,
-                             TypeCreationFunction func,
-                             const char *containerName)
+                             TypeCreationFunction func)
 {
-    // This version could be delayed as well, but for the few cases
-    // we simply fetch the container type and insert directly.
-    AutoDecRef obContainerType(PyObject_GetAttrString(module, containerName));
-    PyTypeObject *type = func(obContainerType);
-    PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type));   // steals reference
-}
+    // - locate the module in the moduleTofuncs mapping
+    auto tableIter = moduleToFuncs.find(module);
+    assert(tableIter != moduleToFuncs.end());
+    // - Assign the name/generating function tcStruct.
+    auto &nameToFunc = tableIter->second;
+    TypeCreationStruct tcStruct{func, {}};
+    auto nit = nameToFunc.find(name);
+    if (nit == nameToFunc.end())
+        nameToFunc.insert(std::make_pair(name, tcStruct));
+    else
+        nit->second = tcStruct;
 
-void AddTypeCreationFunction(PyObject *module,
-                             const char *name,
-                             TypeCreationFunction func,
-                             const char *outerContainerName,
-                             const char *innerContainerName)
-{
-    // This version has even more indirection. It is very rare, and
-    // we handle it directly.
-    AutoDecRef obOuterType(PyObject_GetAttrString(module, outerContainerName));
-    AutoDecRef obInnerType(PyObject_GetAttrString(obOuterType, innerContainerName));
-    PyTypeObject *type = func(obInnerType);
-    PyObject_SetAttrString(obInnerType, name, reinterpret_cast<PyObject *>(type));   // steals reference
+    checkIfShouldLoadImmediately(module, name, nameToFunc);
 }
 
 void AddTypeCreationFunction(PyObject *module,
-                             const char *name,
+                             const char *containerName,
                              TypeCreationFunction func,
-                             const char *containerName3,
-                             const char *containerName2,
-                             const char *containerName)
+                             const char *namePath)
 {
-    // This version has even mode indirection. It is very rare, and
-    // we handle it directly.
-    AutoDecRef obContainerType3(PyObject_GetAttrString(module, containerName3));
-    AutoDecRef obContainerType2(PyObject_GetAttrString(obContainerType3, containerName2));
-    AutoDecRef obContainerType(PyObject_GetAttrString(obContainerType2, containerName));
-    PyTypeObject *type = func(obContainerType);
-    PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type));   // steals reference
+    // - locate the module in the moduleTofuncs mapping
+    auto tableIter = moduleToFuncs.find(module);
+    assert(tableIter != moduleToFuncs.end());
+    // - Assign the name/generating function tcStruct.
+    auto &nameToFunc = tableIter->second;
+    auto nit = nameToFunc.find(containerName);
+
+    // - insert namePath into the subtype vector of the main type.
+    nit->second.subtypeNames.emplace_back(namePath);
+    // - insert it also as its own entry.
+    nit = nameToFunc.find(namePath);
+    TypeCreationStruct tcStruct{func, {}};
+    if (nit == nameToFunc.end())
+        nameToFunc.insert(std::make_pair(namePath, tcStruct));
+    else
+        nit->second = tcStruct;
+
+    checkIfShouldLoadImmediately(module, namePath, nameToFunc);
 }
 
 PyObject *import(const char *moduleName)
@@ -458,7 +499,6 @@ PyObject *create(const char * /* modName */, void *moduleData)
         Py_INCREF(origImportFunc);
         AutoDecRef func(PyCFunction_NewEx(lazy_methods, nullptr, nullptr));
         PyDict_SetItemString(builtins, "__import__", func);
-        // Everything is set.
         lazy_init = true;
     }
     // PYSIDE-2404: Nuitka inserts some additional code in standalone mode
@@ -499,4 +539,29 @@ SbkConverter **getTypeConverters(PyObject *module)
     return (iter == moduleConverters.end()) ? 0 : iter->second;
 }
 
+// Replace the dictionary of a module by a different dict.
+// The dict should be filled with the content of the old dict, before.
+// Reason: Creating a module dict with __missing__ support.
+typedef struct {
+    PyObject_HEAD
+    PyObject *md_dict;
+} StartOf_PyModuleObject;
+
+bool replaceModuleDict(PyObject *module, PyObject *modClass, PyObject *dict)
+{
+    if (!(PyModule_Check(module) && PyType_Check(modClass) && PyDict_Check(dict)))
+        return false;
+    auto *modict = PyModule_GetDict(module);
+    auto *modIntern = reinterpret_cast<StartOf_PyModuleObject *>(module);
+    if (modict != modIntern->md_dict)
+        Py_FatalError("The layout of modules is incompatible");
+    auto *hold = modIntern->md_dict;
+    modIntern->md_dict = dict;
+    Py_INCREF(dict);
+    Py_DECREF(hold);
+    Py_INCREF(modClass);
+    module->ob_type = reinterpret_cast<PyTypeObject *>(modClass);
+    return true;
+}
+
 } } // namespace Shiboken::Module
index 1b3de33b736e2f83d78744f7e4aed68a3f4a1b42..ac77604b839f7ed51dac515a165575dcb75f3fbc 100644 (file)
@@ -56,18 +56,6 @@ LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
                                              TypeCreationFunction func,
                                              const char *containerName);
 
-LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
-                                             const char *name,
-                                             TypeCreationFunction func,
-                                             const char *outerContainerName,
-                                             const char *innerContainerName);
-
-LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
-                                             const char *name,
-                                             TypeCreationFunction func,
-                                             const char *containerName3,
-                                             const char *containerName2,
-                                             const char *containerName);
 /**
  *  Registers the list of types created by \p module.
  *  \param module   Module where the types were created.
@@ -96,6 +84,11 @@ LIBSHIBOKEN_API void registerTypeConverters(PyObject *module, SbkConverter **con
  */
 LIBSHIBOKEN_API SbkConverter **getTypeConverters(PyObject *module);
 
+/**
+ *  Replace the dictionary of a module. This allows to use `__missing__`.
+ */
+LIBSHIBOKEN_API bool replaceModuleDict(PyObject *module, PyObject *modClass, PyObject *dict);
+
 } // namespace Shiboken::Module
 
 #endif // SBK_MODULE_H
index e62fa13ae580f04aacf8f98805f986b2f361bced..4914bec4d20a2386138455ab030b88148ab82764 100644 (file)
@@ -6,32 +6,20 @@
 
 #include "sbkversion.h"
 
+// PYSIDE-2701: This definition is needed for all Python formats with "#".
+#define PY_SSIZE_T_CLEAN
+
 // Qt's "slots" macro collides with the "slots" member variables
 // used in some Python structs. For compilers that support push_macro,
 // temporarily undefine it.
 #if defined(slots) && (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__))
 #  pragma push_macro("slots")
 #  undef slots
-/*
- * Python 2 has function _Py_Mangle directly in Python.h .
- * This creates wrong language binding unless we define 'extern "C"' here.
- */
-extern "C" {
-/*
- * Python 2 uses the "register" keyword, which is deprecated in C++ 11
- * and forbidden in C++17.
- */
-#  if defined(__clang__)
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wdeprecated-register"
-#  endif
 
+extern "C" {
 #  include <Python.h>
-
-#  if defined(__clang__)
-#    pragma clang diagnostic pop
-#  endif
 }
+
 #  include <structmember.h>
 // Now we have the usual variables from Python.h .
 #  include "shibokenmacros.h"
@@ -42,21 +30,9 @@ extern "C" {
 #else
 
 extern "C" {
-/*
- * Python 2 uses the "register" keyword, which is deprecated in C++ 11
- * and forbidden in C++17.
- */
-#  if defined(__clang__)
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wdeprecated-register"
-#  endif
-
 #  include <Python.h>
-
-#  if defined(__clang__)
-#    pragma clang diagnostic pop
-#  endif
 }
+
 #  include <structmember.h>
 // Now we have the usual variables from Python.h .
 #  include "shibokenmacros.h"
index 1471cd7fe141c1f2ce21a9ccf4f0fb881f034477..6053772cfd4711b4fcd554f0788ae55b7ba9af9b 100644 (file)
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
 #include "sbkstring.h"
+#include "sbkenum.h"
 #include "sbkstaticstrings_p.h"
 #include "autodecref.h"
 
@@ -14,6 +15,11 @@ bool checkIterable(PyObject *obj)
     return PyObject_HasAttr(obj, Shiboken::PyMagicName::iter());
 }
 
+bool checkIterableArgument(PyObject *obj)
+{
+    return checkIterable(obj) && !Shiboken::Enum::check(obj);
+}
+
 static PyObject *initPathLike()
 {
     PyObject *PathLike{};
@@ -75,7 +81,7 @@ const char *toCString(PyObject *str)
     if (PyUnicode_Check(str))
         return _PepUnicode_AsString(str);
     if (PyBytes_Check(str))
-        return PyBytes_AS_STRING(str);
+        return PyBytes_AsString(str);
     return nullptr;
 }
 
@@ -88,14 +94,14 @@ const char *toCString(PyObject *str, Py_ssize_t *len)
     if (PyUnicode_Check(str)) {
        // We need to encode the unicode string into utf8 to know the size of returned char *.
        Shiboken::AutoDecRef uniStr(PyUnicode_AsUTF8String(str));
-       *len = PyBytes_GET_SIZE(uniStr.object());
+       *len = PyBytes_Size(uniStr.object());
         // Return unicode from str instead of uniStr, because the lifetime of the returned pointer
         // depends on the lifetime of str.
         return _PepUnicode_AsString(str);
     }
     if (PyBytes_Check(str)) {
-        *len = PyBytes_GET_SIZE(str);
-        return PyBytes_AS_STRING(str);
+        *len = PyBytes_Size(str);
+        return PyBytes_AsString(str);
     }
     return nullptr;
 }
@@ -146,10 +152,10 @@ Py_ssize_t len(PyObject *str)
         return 0;
 
     if (PyUnicode_Check(str))
-        return PepUnicode_GetLength(str);
+        return PyUnicode_GetLength(str);
 
     if (PyBytes_Check(str))
-        return PyBytes_GET_SIZE(str);
+        return PyBytes_Size(str);
     return 0;
 }
 
index f91847c112105114a345e9f49000d38728de06d1..3ff2805e23a9495ee2e59806fa28785fbac292e9 100644 (file)
@@ -7,12 +7,12 @@
 #include "sbkpython.h"
 #include "shibokenmacros.h"
 
-namespace Shiboken
-{
-namespace String
+namespace Shiboken::String
 {
     LIBSHIBOKEN_API bool check(PyObject *obj);
     LIBSHIBOKEN_API bool checkIterable(PyObject *obj);
+    /// Check for iterable function arguments (excluding enumerations)
+    LIBSHIBOKEN_API bool checkIterableArgument(PyObject *obj);
     LIBSHIBOKEN_API bool checkPath(PyObject *path);
     LIBSHIBOKEN_API bool checkType(PyTypeObject *obj);
     LIBSHIBOKEN_API bool checkChar(PyObject *obj);
@@ -31,10 +31,6 @@ namespace String
     LIBSHIBOKEN_API PyObject *getSnakeCaseName(PyObject *name, bool lower);
     LIBSHIBOKEN_API PyObject *repr(PyObject *o);
 
-} // namespace String
-} // namespace Shiboken
-
+} // namespace Shiboken::String
 
 #endif
-
-
index 079548eed1d764be6df6c580cdba696da2515d26..f8d8b910448769f79cfc60b9e4eb44929f6975c7 100644 (file)
@@ -63,9 +63,8 @@ static PyObject *_PyType_FromSpecWithBasesHack(PyType_Spec *spec,
             bases = basesPatch.object();
         }
 
-        Py_ssize_t n = PyTuple_GET_SIZE(bases);
-        for (auto idx = 0; idx < n; ++idx) {
-            PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, idx));
+        for (Py_ssize_t idx = 0, n = PyTuple_Size(bases); idx < n; ++idx) {
+            PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, idx));
             PyTypeObject *meta = Py_TYPE(base);
             if (meta->tp_new != PyType_Type.tp_new) {
                 // make sure there is no second meta class
@@ -87,7 +86,7 @@ static PyObject *_PyType_FromSpecWithBasesHack(PyType_Spec *spec,
         keepMeta->tp_new = keepNew;
     if (basesPatch.object()) {
         // undo the metaclass patch.
-        auto *base = PyTuple_GET_ITEM(basesPatch.object(), 0);
+        auto *base = PyTuple_GetItem(basesPatch.object(), 0);
         base->ob_type = &PyType_Type;
     }
     return ret;
@@ -273,7 +272,7 @@ static PyTypeObject *
 best_base(PyObject *bases)
 {
     // We always have only one base
-    return reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, 0));
+    return reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, 0));
 }
 
 static PyObject *
index fcf777ae0e41a9c06cf7ee2de421812fd8de86aa..0e48364c4aec70a4c330f93c749d3307d68cc192 100644 (file)
@@ -19,9 +19,9 @@
 #include "sbkmodule.h"
 #include "sbkstring.h"
 #include "sbkstaticstrings.h"
+#include "sbktypefactory.h"
 #include "shibokenmacros.h"
 #include "shibokenbuffer.h"
 #include "signature.h"
 
 #endif // SHIBOKEN_H
-
index 6b17eb6ebac1b2a30792be69c451364bd351e324..068a5e9fd0f22a6cf04f8755e65c18803c1ad939 100644 (file)
@@ -7,10 +7,7 @@
 #include "sbkpython.h"
 #include "shibokenmacros.h"
 
-namespace Shiboken
-{
-
-namespace Buffer
+namespace Shiboken::Buffer
 {
     enum Type {
         ReadOnly,
@@ -51,7 +48,6 @@ namespace Buffer
      */
     LIBSHIBOKEN_API void *copyData(PyObject *pyObj, Py_ssize_t *size = nullptr);
 
-} // namespace Buffer
-} // namespace Shiboken
+} // namespace Shiboken::Buffer
 
 #endif
index e0130b5a66b933f6f730729d4176122f9a296609..893fba990be8e4d2dee3f344a6563fb05947afc2 100644 (file)
@@ -11,7 +11,9 @@ extern "C"
 {
 
 LIBSHIBOKEN_API int InitSignatureStrings(PyTypeObject *, const char *[]);
-LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *[]);
+LIBSHIBOKEN_API int InitSignatureBytes(PyTypeObject *, const uint8_t[], size_t);
+LIBSHIBOKEN_API int FinishSignatureInitialization(PyObject *, const char *[]);
+LIBSHIBOKEN_API int FinishSignatureInitBytes(PyObject *, const uint8_t [], size_t);
 LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *, PyObject *);
 LIBSHIBOKEN_API PyObject *Sbk_TypeGet___doc__(PyObject *);
 LIBSHIBOKEN_API PyObject *GetFeatureDict();
index 3255cb56d1cce8ed2ae93cc2b20f5ba622fa00eb..81f464cd3020889745a735e84782e83c3ed2e3e4 100644 (file)
@@ -25,6 +25,8 @@
 
 #include <structmember.h>
 
+#include <algorithm>
+
 using namespace Shiboken;
 
 extern "C"
@@ -266,9 +268,24 @@ static PyObject *get_signature(PyObject * /* self */, PyObject *args)
     PyObject *ret = get_signature_intern(ob, modifier);
     if (ret != nullptr)
         return ret;
+    if (PyErr_Occurred())
+        return nullptr;
     Py_RETURN_NONE;
 }
 
+////////////////////////////////////////////////////////////////////////////
+//
+// make_snake_case_name  --  make efficient name change available in Python
+//
+//
+
+static PyObject *make_snake_case_name(PyObject * /* self */, PyObject *arg)
+{
+    if (!PyUnicode_Check(arg))
+        return PyErr_Format(PyExc_TypeError, "%S is not a string");
+    return Shiboken::String::getSnakeCaseName(arg, true);
+}
+
 ////////////////////////////////////////////////////////////////////////////
 //
 // feature_import  --  special handling for `from __feature__ import ...`
@@ -305,9 +322,12 @@ static PyObject *feature_import(PyObject * /* self */, PyObject *args, PyObject
 }
 
 PyMethodDef signature_methods[] = {
-    {"__feature_import__", (PyCFunction)feature_import, METH_VARARGS | METH_KEYWORDS, nullptr},
-    {"get_signature", (PyCFunction)get_signature, METH_VARARGS,
+    {"__feature_import__", reinterpret_cast<PyCFunction>(feature_import),
+        METH_VARARGS | METH_KEYWORDS, nullptr},
+    {"get_signature", reinterpret_cast<PyCFunction>(get_signature), METH_VARARGS,
         "get the signature, passing an optional string parameter"},
+    {"make_snake_case_name", reinterpret_cast<PyCFunction>(make_snake_case_name), METH_O,
+        "turn a camelCase name into snake_case"},
     {nullptr, nullptr, 0, nullptr}
 };
 
@@ -339,7 +359,7 @@ static int PySide_BuildSignatureArgs(PyObject *obtype_mod, const char *signature
      * address of a string array. It will not be turned into a real
      * string list until really used by Python. This is quite optimal.
      */
-    AutoDecRef numkey(Py_BuildValue("n", signatures));
+    AutoDecRef numkey(PyLong_FromVoidPtr(signatures));
     if (type_key.isNull() || numkey.isNull()
         || PyDict_SetItem(pyside_globals->arg_dict, type_key, numkey) < 0)
         return -1;
@@ -350,6 +370,75 @@ static int PySide_BuildSignatureArgs(PyObject *obtype_mod, const char *signature
     return PyDict_SetItem(pyside_globals->map_dict, type_key, obtype_mod) == 0 ? 0 : -1;
 }
 
+static int PySide_BuildSignatureArgsByte(PyObject *obtype_mod, const uint8_t *signatures,
+                                         size_t size)
+{
+    AutoDecRef type_key(GetTypeKey(obtype_mod));
+    AutoDecRef numkey(PyTuple_New(2));
+    PyTuple_SetItem(numkey.object(), 0, PyLong_FromVoidPtr(const_cast<uint8_t *>(signatures)));
+    PyTuple_SetItem(numkey.object(), 1, PyLong_FromSize_t(size));
+    if (type_key.isNull() || numkey.isNull()
+        || PyDict_SetItem(pyside_globals->arg_dict, type_key, numkey) < 0)
+        return -1;
+    return PyDict_SetItem(pyside_globals->map_dict, type_key, obtype_mod) == 0 ? 0 : -1;
+}
+
+// PYSIDE-2701: MS cannot use the name "_expand".
+static PyObject *byteExpand(PyObject *packed)
+{
+    const char commonMsg[] = "Please disable compression by passing  --unoptimize=compression";
+
+    static PyObject *compressModule = PyImport_ImportModule("zlib");
+    if (compressModule == nullptr)
+        return PyErr_Format(PyExc_ImportError,
+                            "The zlib module cannot be imported. %s", commonMsg);
+
+    static PyObject *expandFunc = PyObject_GetAttrString(compressModule, "decompress");
+    if (expandFunc == nullptr)
+        return PyErr_Format(PyExc_NameError,
+                            "The expand function of zlib was not fount. %s", commonMsg);
+    PyObject *unpacked = PyObject_CallFunctionObjArgs(expandFunc, packed, nullptr);
+    if (unpacked == nullptr)
+        return PyErr_Format(PyExc_ValueError,
+                            "Some packed strings could not be unpacked. %s", commonMsg);
+    return unpacked;
+}
+
+const char **bytesToStrings(const uint8_t signatures[], Py_ssize_t size)
+{
+    // PYSIDE-2701: Unpack a ZLIB compressed string.
+    // The result is a single char* with newlines after each string. Convert
+    // this into single char* objects that InitSignatureStrings expects.
+
+    const auto *chars = reinterpret_cast<const char *>(signatures);
+    PyObject *packed = PyBytes_FromStringAndSize(chars, size);
+
+    // The Qt compressor treats empty arrays specially.
+    PyObject *data = size > 0 ? byteExpand(packed) : PyBytes_FromStringAndSize(chars, 0);
+    if (data == nullptr)
+        return nullptr;
+
+    char *cdata{};
+    Py_ssize_t len{};
+    PyBytes_AsStringAndSize(data, &cdata, &len);
+    char *cdataEnd = cdata + len;
+    size_t nlines = std::count(cdata, cdataEnd, '\n');
+
+    char **lines = new char *[nlines + 1];
+    int pos = 0;
+    char *line = cdata;
+
+    for (char *p = cdata; p < cdataEnd; ++p) {
+        if (*p == '\n') {
+            lines[pos++] = line;
+            *p = 0;
+            line = p + 1;
+        }
+    }
+    lines[pos] = nullptr;
+    return const_cast<const char **>(lines);
+}
+
 PyObject *PySide_BuildSignatureProps(PyObject *type_key)
 {
     /*
@@ -360,8 +449,20 @@ PyObject *PySide_BuildSignatureProps(PyObject *type_key)
      */
     if (type_key == nullptr)
         return nullptr;
+    AutoDecRef strings{};
     PyObject *numkey = PyDict_GetItem(pyside_globals->arg_dict, type_key);
-    AutoDecRef strings(_address_to_stringlist(numkey));
+    if (PyTuple_Check(numkey)) {
+        PyObject *obAddress = PyTuple_GetItem(numkey, 0);
+        PyObject *obSize = PyTuple_GetItem(numkey, 1);
+        const void *addr = PyLong_AsVoidPtr(obAddress);
+        const Py_ssize_t size = PyLong_AsSsize_t(obSize);
+        const char **cstrings = bytesToStrings(reinterpret_cast<const uint8_t *>(addr), size);
+        if (cstrings == nullptr)
+            return nullptr;
+        strings.reset(_address_ptr_to_stringlist(cstrings));
+    } else {
+        strings.reset(_address_to_stringlist(numkey));
+    }
     if (strings.isNull())
         return nullptr;
     AutoDecRef arg_tup(Py_BuildValue("(OO)", type_key, strings.object()));
@@ -398,24 +499,8 @@ static bool get_lldebug_flag()
 
 #endif
 
-static int PySide_FinishSignatures(PyObject *module, const char *signatures[])
+static int _finishSignaturesCommon(PyObject *module)
 {
-#ifdef PYPY_VERSION
-    static const bool have_problem = get_lldebug_flag();
-    if (have_problem)
-        return 0; // crash with lldebug at `PyDict_Next`
-#endif
-    /*
-     * Initialization of module functions and resolving of static methods.
-     */
-    const char *name = PyModule_GetName(module);
-    if (name == nullptr)
-        return -1;
-
-    // we abuse the call for types, since they both have a __name__ attribute.
-    if (PySide_BuildSignatureArgs(module, signatures) < 0)
-        return -1;
-
     /*
      * Note: This function crashed when called from PySide_BuildSignatureArgs.
      * Probably this was an import timing problem.
@@ -424,25 +509,66 @@ static int PySide_FinishSignatures(PyObject *module, const char *signatures[])
      * to the PyCFunction attributes. Therefore I simplified things
      * and always use our own mapping.
      */
-    PyObject *key, *func, *obdict = PyModule_GetDict(module);
+    PyObject *key{};
+    PyObject *func{};
+    PyObject *obdict = PyModule_GetDict(module);
     Py_ssize_t pos = 0;
 
-    while (PyDict_Next(obdict, &pos, &key, &func))
+    // Here we collect all global functions to finish our mapping.
+    while (PyDict_Next(obdict, &pos, &key, &func)) {
         if (PyCFunction_Check(func))
             if (PyDict_SetItem(pyside_globals->map_dict, func, module) < 0)
                 return -1;
+    }
     // The finish_import function will not work the first time since phase 2
     // was not yet run. But that is ok, because the first import is always for
     // the shiboken module (or a test module).
+    [[maybe_unused]] const char *name = PyModule_GetName(module);
     if (pyside_globals->finish_import_func == nullptr) {
         assert(strncmp(name, "PySide6.", 8) != 0);
         return 0;
     }
+    // Call a Python function which has to finish something as well.
     AutoDecRef ret(PyObject_CallFunction(
         pyside_globals->finish_import_func, "(O)", module));
     return ret.isNull() ? -1 : 0;
 }
 
+static int PySide_FinishSignatures(PyObject *module, const char *signatures[])
+{
+#ifdef PYPY_VERSION
+    static const bool have_problem = get_lldebug_flag();
+    if (have_problem)
+        return 0; // crash with lldebug at `PyDict_Next`
+#endif
+
+    // Initialization of module functions and resolving of static methods.
+    const char *name = PyModule_GetName(module);
+    if (name == nullptr)
+        return -1;
+
+    // we abuse the call for types, since they both have a __name__ attribute.
+    if (PySide_BuildSignatureArgs(module, signatures) < 0)
+        return -1;
+    return _finishSignaturesCommon(module);
+}
+
+static int PySide_FinishSignaturesByte(PyObject *module, const uint8_t signatures[], size_t size)
+{
+#ifdef PYPY_VERSION
+    static const bool have_problem = get_lldebug_flag();
+    if (have_problem)
+        return 0; // crash with lldebug at `PyDict_Next`
+#endif
+    const char *name = PyModule_GetName(module);
+    if (name == nullptr)
+        return -1;
+
+    if (PySide_BuildSignatureArgsByte(module, signatures, size) < 0)
+        return -1;
+    return _finishSignaturesCommon(module);
+}
+
 ////////////////////////////////////////////////////////////////////////////
 //
 // External functions interface
@@ -464,7 +590,21 @@ int InitSignatureStrings(PyTypeObject *type, const char *signatures[])
     return ret;
 }
 
-void FinishSignatureInitialization(PyObject *module, const char *signatures[])
+int InitSignatureBytes(PyTypeObject *type, const uint8_t signatures[], size_t size)
+{
+    // PYSIDE-2701: Store the compressed bytes and produce input for
+    //              InitSignatureStrings later.
+    init_shibokensupport_module();
+    auto *obType = reinterpret_cast<PyObject *>(type);
+    const int ret = PySide_BuildSignatureArgsByte(obType, signatures, size);
+    if (ret < 0 || _build_func_to_type(obType) < 0) {
+        PyErr_Print();
+        PyErr_SetNone(PyExc_ImportError);
+    }
+    return ret;
+}
+
+int FinishSignatureInitialization(PyObject *module, const char *signatures[])
 {
     /*
      * This function is called at the very end of a module initialization.
@@ -489,6 +629,26 @@ void FinishSignatureInitialization(PyObject *module, const char *signatures[])
         PyErr_Print();
         PyErr_SetNone(PyExc_ImportError);
     }
+    return 0;
+}
+
+int FinishSignatureInitBytes(PyObject *module, const uint8_t signatures[], size_t size)
+{
+    init_shibokensupport_module();
+
+#ifndef PYPY_VERSION
+    static const bool patch_types = true;
+#else
+    // PYSIDE-535: On PyPy we cannot patch builtin types. This can be
+    //             re-implemented later. For now, we use `get_signature`, instead.
+    static const bool patch_types = false;
+#endif
+
+    if ((patch_types && PySide_PatchTypes() < 0)
+        || PySide_FinishSignaturesByte(module, signatures, size) < 0) {
+        return -1;
+    }
+    return 0;
 }
 
 static PyObject *adjustFuncName(const char *func_name)
@@ -538,7 +698,7 @@ static PyObject *adjustFuncName(const char *func_name)
     assert(PyType_Check(obtype));   // This was not true for __init__!
 
     // Find the feature flags
-    auto type = reinterpret_cast<PyTypeObject *>(obtype.object());
+    auto *type = reinterpret_cast<PyTypeObject *>(obtype.object());
     AutoDecRef dict(PepType_GetDict(type));
     int id = currentSelectId(type);
     id = id < 0 ? 0 : id;   // if undefined, set to zero
@@ -585,7 +745,9 @@ void SetError_Argument(PyObject *args, const char *func_name, PyObject *info)
 
     // PYSIDE-1305: Handle errors set by fillQtProperties.
     if (PyErr_Occurred()) {
-        PyObject *e, *v, *t;
+        PyObject *e{};
+        PyObject *v{};
+        PyObject *t{};
         // Note: These references are all borrowed.
         PyErr_Fetch(&e, &v, &t);
         Py_DECREF(e);
@@ -606,7 +768,8 @@ void SetError_Argument(PyObject *args, const char *func_name, PyObject *info)
         PyErr_Print();
         Py_FatalError("seterror_argument did not receive a result");
     }
-    PyObject *err, *msg;
+    PyObject *err{};
+    PyObject *msg{};
     if (!PyArg_UnpackTuple(res, func_name, 2, 2, &err, &msg)) {
         PyErr_Print();
         Py_FatalError("unexpected failure in seterror_argument");
index 7292f8216831987380052bd65e2c389f0423a0fb..3ce9e21e379a23b1cab6939722ac555612b1fd4e 100644 (file)
@@ -120,12 +120,10 @@ static int handle_doc_in_progress = 0;
 static PyObject *handle_doc(PyObject *ob, PyObject *old_descr)
 {
     AutoDecRef ob_type_mod(GetClassOrModOf(ob));
-    const char *name;
     bool isModule = PyModule_Check(ob_type_mod.object());
-    if (isModule)
-        name = PyModule_GetName(ob_type_mod.object());
-    else
-        name = reinterpret_cast<PyTypeObject *>(ob_type_mod.object())->tp_name;
+    const char *name = isModule
+        ? PyModule_GetName(ob_type_mod.object())
+        : reinterpret_cast<PyTypeObject *>(ob_type_mod.object())->tp_name;
     PyObject *res{};
 
     if (handle_doc_in_progress || name == nullptr
@@ -205,14 +203,14 @@ int PySide_PatchTypes(void)
         AutoDecRef wrap_descr(PyObject_GetAttrString(
                                     reinterpret_cast<PyObject *>(Py_TYPE(Py_True)), "__add__"));
         // abbreviations for readability
-        auto md_gs = new_PyMethodDescr_getsets;
-        auto md_doc = &old_md_doc_descr;
-        auto cf_gs = new_PyCFunction_getsets;
-        auto cf_doc = &old_cf_doc_descr;
-        auto sm_gs = new_PyStaticMethod_getsets;
-        auto sm_doc = &old_sm_doc_descr;
-        auto wd_gs = new_PyWrapperDescr_getsets;
-        auto wd_doc = &old_wd_doc_descr;
+        auto *md_gs = new_PyMethodDescr_getsets;
+        auto *md_doc = &old_md_doc_descr;
+        auto *cf_gs = new_PyCFunction_getsets;
+        auto *cf_doc = &old_cf_doc_descr;
+        auto *sm_gs = new_PyStaticMethod_getsets;
+        auto *sm_doc = &old_sm_doc_descr;
+        auto *wd_gs = new_PyWrapperDescr_getsets;
+        auto *wd_doc = &old_wd_doc_descr;
 
         if (meth_descr.isNull() || wrap_descr.isNull()
             || PyType_Ready(Py_TYPE(meth_descr)) < 0
index 3a79a12d5a30ddd923f4ff2efb67e740b4b37741..0a08309cc01431e7c7f4e1b358ca4a756faf457f 100644 (file)
@@ -120,7 +120,7 @@ static safe_globals_struc *init_phase_1()
 
         return p;
 
-    } while (0);
+    } while (false);
 
     PyErr_Print();
     Py_FatalError("could not initialize part 1");
@@ -130,10 +130,8 @@ static safe_globals_struc *init_phase_1()
 static int init_phase_2(safe_globals_struc *p, PyMethodDef *methods)
 {
     do {
-        PyMethodDef *ml;
-
         // The single function to be called, but maybe more to come.
-        for (ml = methods; ml->ml_name != nullptr; ml++) {
+        for (PyMethodDef *ml = methods; ml->ml_name != nullptr; ++ml) {
             PyObject *v = PyCFunction_NewEx(ml, nullptr, nullptr);
             if (v == nullptr
                 || PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0)
@@ -220,10 +218,8 @@ static int init_phase_2(safe_globals_struc *p, PyMethodDef *methods)
 static void handler(int sig) {
 #if defined(__GLIBC__)
     void *array[30];
-    size_t size;
-
     // get void *'s for all entries on the stack
-    size = backtrace(array, 30);
+    const int size = backtrace(array, 30);
 
     // print out all the frames to stderr
 #endif
index cf84cfa13e64069dda8b5fe1ddeeae488e496729..04b411825de2f3fcc1080462664364ae40c62650 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "signature_p.h"
 
+#include <cstring>
+
 using namespace Shiboken;
 
 extern "C" {
@@ -78,7 +80,6 @@ int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr)
         if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
             return -1;
     }
-    PyType_Modified(type);
     return 0;
 }
 
@@ -115,11 +116,11 @@ static PyObject *_func_with_new_name(PyTypeObject *type,
      * but does not create a descriptor.
      * XXX Maybe we can get rid of this, completely?
      */
-    auto obtype = reinterpret_cast<PyObject *>(type);
-    int len = strlen(new_name);
-    auto name = new char[len + 1];
-    strcpy(name, new_name);
-    auto new_meth = new PyMethodDef;
+    auto *obtype = reinterpret_cast<PyObject *>(type);
+    const size_t len = std::strlen(new_name);
+    auto *name = new char[len + 1];
+    std::strcpy(name, new_name);
+    auto *new_meth = new PyMethodDef;
     new_meth->ml_name = name;
     new_meth->ml_meth = meth->ml_meth;
     new_meth->ml_flags = meth->ml_flags;
@@ -196,8 +197,8 @@ static PyObject *_build_new_entry(PyObject *new_name, PyObject *value)
         if (list.isNull())
             return nullptr;
         for (int idx = 0; idx < len; ++idx) {
-            auto multi_entry = PyList_GetItem(multi, idx);
-            auto dup = PyDict_Copy(multi_entry);
+            auto *multi_entry = PyList_GetItem(multi, idx);
+            auto *dup = PyDict_Copy(multi_entry);
             if (PyDict_SetItem(dup, PyName::name(), new_name) < 0)
                 return nullptr;
             if (PyList_SetItem(list, idx, dup) < 0)
@@ -215,7 +216,8 @@ static PyObject *_build_new_entry(PyObject *new_name, PyObject *value)
 int insert_snake_case_variants(PyObject *dict)
 {
     AutoDecRef snake_dict(PyDict_New());
-    PyObject *key, *value;
+    PyObject *key{};
+    PyObject *value{};
     Py_ssize_t pos = 0;
     while (PyDict_Next(dict, &pos, &key, &value)) {
         AutoDecRef name(String::getSnakeCaseName(key, true));
@@ -269,24 +271,13 @@ PyObject *_get_class_of_descr(PyObject *ob)
     return PyObject_GetAttr(ob, PyMagicName::objclass());
 }
 
-PyObject *_address_to_stringlist(PyObject *numkey)
+PyObject *_address_ptr_to_stringlist(const char **sig_strings)
 {
-    /*
-     * This is a tiny optimization that saves initialization time.
-     * Instead of creating all Python strings during the call to
-     * `PySide_BuildSignatureArgs`, we store the address of the stringlist.
-     * When needed in `PySide_BuildSignatureProps`, the strings are
-     * finally materialized.
-     */
-    Py_ssize_t address = PyNumber_AsSsize_t(numkey, PyExc_ValueError);
-    if (address == -1 && PyErr_Occurred())
-        return nullptr;
-    char **sig_strings = reinterpret_cast<char **>(address);
     PyObject *res_list = PyList_New(0);
     if (res_list == nullptr)
         return nullptr;
     for (; *sig_strings != nullptr; ++sig_strings) {
-        char *sig_str = *sig_strings;
+        const char *sig_str = *sig_strings;
         AutoDecRef pystr(Py_BuildValue("s", sig_str));
         if (pystr.isNull() || PyList_Append(res_list, pystr) < 0)
             return nullptr;
@@ -294,6 +285,21 @@ PyObject *_address_to_stringlist(PyObject *numkey)
     return res_list;
 }
 
+PyObject *_address_to_stringlist(PyObject *numkey)
+{
+    /*
+     * This is a tiny optimization that saves initialization time.
+     * Instead of creating all Python strings during the call to
+     * `PySide_BuildSignatureArgs`, we store the address of the stringlist.
+     * When needed in `PySide_BuildSignatureProps`, the strings are
+     * finally materialized.
+     */
+    void *address = PyLong_AsVoidPtr(numkey);
+    if (address == nullptr && PyErr_Occurred())
+        return nullptr;
+    return _address_ptr_to_stringlist(reinterpret_cast<const char **>(address));
+}
+
 int _build_func_to_type(PyObject *obtype)
 {
     /*
@@ -363,8 +369,8 @@ int _build_func_to_type(PyObject *obtype)
             if (descr == nullptr)
                 return -1;
             char mangled_name[200];
-            strcpy(mangled_name, meth->ml_name);
-            strcat(mangled_name, ".overload");
+            std::strcpy(mangled_name, meth->ml_name);
+            std::strcat(mangled_name, ".overload");
             if (PyDict_SetItemString(dict, mangled_name, descr) < 0)
                 return -1;
             if (meth->ml_flags & METH_STATIC) {
index d0c4ee537e5f750631ecb5468f53dfd4c37cd090..3433034bef907ffab0abb6d6f44ac07d181e17ec 100644 (file)
@@ -63,6 +63,7 @@ PyObject *_get_class_of_cf(PyObject *ob_cf);
 PyObject *_get_class_of_sm(PyObject *ob_sm);
 PyObject *_get_class_of_descr(PyObject *ob);
 PyObject *_address_to_stringlist(PyObject *numkey);
+PyObject *_address_ptr_to_stringlist(const char **sig_strings);
 int _build_func_to_type(PyObject *obtype);
 int _finish_nested_classes(PyObject *dict);
 
index 9f74ed442f6e0c3e736464a0ba63fa2afdc981be..998725d0cfed6c97ca7d3470b892a253c24def40 100644 (file)
@@ -28,4 +28,3 @@ void ThreadStateSaver::restore()
 }
 
 } // namespace Shiboken
-
index 4289f6726b8f0c57cf90ad6f74dd036583b8d921..86bd7ac3b99b834fa9a97618dc7e925099314f84 100644 (file)
@@ -29,4 +29,3 @@ private:
 } // namespace Shiboken
 
 #endif // THREADSTATESAVER_H
-
index 8bb3f6ac88b2a7a78bbb963d8b1db6f3c35bb377..ce85d4946c018287d521c6f8369e160582d0096a 100644 (file)
@@ -6,6 +6,7 @@
 #include "sbkconverter.h"
 #include "basewrapper.h"
 #include "basewrapper_p.h"
+#include "sbktypefactory.h"
 
 extern "C"
 {
@@ -41,7 +42,7 @@ PyObject *SbkVoidPtrObject_new(PyTypeObject *type, PyObject * /* args */, PyObje
 
 int SbkVoidPtrObject_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    PyObject *addressObject;
+    PyObject *addressObject{};
     Py_ssize_t size = -1;
     int isWritable = 0;
     auto *sbkSelf = reinterpret_cast<SbkVoidPtrObject *>(self);
index 30d334f44d981b14067fd44ab982f4ad69f0ad5a..2b69a1c6a0aea7b396bff8c5858ffba2dc45769d 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 import sys
 import os
 import subprocess
index 07b2473164eb11160eca46fac887653e10b7d351..033a2e43967dff00ccf23f8fe5b5c4783795c52b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 major_version = "@shiboken_MAJOR_VERSION@"
 minor_version = "@shiboken_MINOR_VERSION@"
index 6a1a632179a93be3b2370283176fa447a3ca497c..26fe9396e11c1d30a879374954348a603f21d22e 100644 (file)
@@ -13,12 +13,12 @@ Shiboken, except for defaults which are replaced by "...".
 from shiboken6 import Shiboken
 
 
-class Object(object):
+class Object:
 
     def __init__(self) -> None: ...
 
 
-class VoidPtr(object):
+class VoidPtr:
     def __init__(self, value: int) -> None: ...
 
 
index e54bec75a5dea9f85f32f0b28549906910b09b78..db4f381f50205371449fe4ef2ba879ecb7a079dc 100644 (file)
@@ -1,4 +1,5 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 # this file intentionally left blank
index 7a0871ee7d0c969451c0393c5ba50f158525969a..4a8ccdbc606f02d032fae3c3286694f8ac43a25f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 # flake8: noqa F:821
 # flake8: noqa F:401
index f7190b12f12a5eae67750597c1d70d55b1a184de..524c1c483a415e2e2700ffb629465471f5fd57a1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 fix-complaints.py
index bebf56c7eb4d270ef52ad836e2395e1139cf6fb8..fa771c95f0d873b16c848108870a0731ccfd3e8d 100644 (file)
@@ -1,4 +1,5 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
-__all__ = "get_signature layout mapping lib".split()
+__all__ = ["get_signature", "make_snake_case_name", "layout", "mapping", "lib"]
index c2a19efef3fb28093c7c2d21ba26d90aabf7ccb3..5ec41aa9f3beae0d6be5bfbcb25adecec98e74f0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 # flake8: noqa E:721
 
index bae26429409d1fc618ada2a4deb4c3759be55649..394e8cda312767327ff522970ed13155b0571009 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 importhandler.py
index 0e781cbcbf0ca0fbd3a235c04735105649527d0b..65f52bde6c036e5f8b233d3188c9f4191372da78 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 layout.py
@@ -22,7 +23,11 @@ import typing
 
 from types import SimpleNamespace
 from textwrap import dedent
-from shibokensupport.signature.mapping import ellipsis
+from shibokensupport.signature.mapping import ellipsis, missing_optional_return
+from shibokensupport.signature.parser import using_snake_case
+from shibokensupport.signature import make_snake_case_name
+
+DEFAULT_PARAM_KIND = inspect.Parameter.POSITIONAL_ONLY
 
 
 class SignatureLayout(SimpleNamespace):
@@ -168,12 +173,148 @@ def make_signature_nameless(signature):
         signature.parameters[key].__class__ = NamelessParameter
 
 
-_POSITIONAL_ONLY         = inspect._POSITIONAL_ONLY  # noqa E:201
-_POSITIONAL_OR_KEYWORD   = inspect._POSITIONAL_OR_KEYWORD  # noqa E:201
-_VAR_POSITIONAL          = inspect._VAR_POSITIONAL  # noqa E:201
-_KEYWORD_ONLY            = inspect._KEYWORD_ONLY  # noqa E:201
-_VAR_KEYWORD             = inspect._VAR_KEYWORD  # noqa E:201
-_empty                   = inspect._empty  # noqa E:201
+_POSITIONAL_ONLY         = inspect.Parameter.POSITIONAL_ONLY        # noqa E:201
+_POSITIONAL_OR_KEYWORD   = inspect.Parameter.POSITIONAL_OR_KEYWORD  # noqa E:201
+_VAR_POSITIONAL          = inspect.Parameter.VAR_POSITIONAL         # noqa E:201
+_KEYWORD_ONLY            = inspect.Parameter.KEYWORD_ONLY           # noqa E:201
+_VAR_KEYWORD             = inspect.Parameter.VAR_KEYWORD            # noqa E:201
+_empty                   = inspect.Parameter.empty                  # noqa E:201
+
+
+default_weights = {
+    typing.Any: 1000,   # noqa E:241
+    bool:        101,   # noqa E:241
+    int:         102,   # noqa E:241
+    float:       103,   # noqa E:241
+    object:      500,   # noqa E:241
+}
+
+
+def get_ordering_key(anno):
+    """
+    This is the main sorting algorithm for annotations.
+    For a normal type, we use the tuple
+
+        (- length of mro(anno), 1, name)
+
+    For Union expressions, we use the minimum
+
+        (- minlen of mro(anno), len(getargs(anno)), name)
+
+    This way, Union annotations are always sorted behind normal types.
+    Addition of a `name` field ensures a unique ordering.
+
+    A special case are numeric types, which have also an ordering between them.
+    They can be handled separately, since they are all of the shortest mro.
+    """
+    typing_type = typing.get_origin(anno)
+    is_union = typing_type is typing.Union
+    if is_union:
+        # This is some Union-like construct.
+        typing_args = typing.get_args(anno)
+        parts = len(typing_args)
+
+        if defaults := list(ann for ann in typing_args if ann in default_weights):
+            # Special: look into the default weights and use the largest.
+            leng = 0
+            for ann in defaults:
+                w = default_weights[ann]
+                if w > leng:
+                    leng = w
+                    anno = ann
+        else:
+            # Normal: Use the union arg with the shortest mro().
+            leng = 9999
+            for ann in typing_args:
+                lng = len(ann.mro())
+                if lng < leng:
+                    leng = lng
+                    anno = ann
+    else:
+        leng = len(anno.mro()) if anno not in (type, None, typing.Any) else 0
+        parts = 1
+    if anno in default_weights:
+        leng = - default_weights[anno]
+    # In 3.10 only None has no name. 3.9 is worse concerning typing constructs.
+    name = anno.__name__ if hasattr(anno, "__name__") else "None"
+    # Put typing containers after the plain type.
+    if typing_type:
+        return (-leng + 100, parts, name)
+    return (-leng, parts, name)
+
+
+def sort_by_inheritance(signatures):
+    # First decorate all signatures with a key built by the mro.
+    for idx, sig in enumerate(signatures):
+        sort_order = []
+        for param in list(sig.parameters.values()):
+            sort_order.append(get_ordering_key(param.annotation))
+        signatures[idx] = sort_order, sig
+
+    # Sort the signatures and remove the key column again.
+    signatures = sorted(signatures, key=lambda x: x[0])
+    for idx, sig in enumerate(signatures):
+        signatures[idx] = sig[1]
+    return signatures
+
+
+def best_to_remove(signatures, idx1, idx2, name=None):
+    # Both have identical annotation.
+    sig1 = signatures[idx1]
+    sig2 = signatures[idx2]
+    ra1 = sig1.return_annotation
+    ra2 = sig2.return_annotation
+    # Both have equal return annotation.
+    if ra1 == ra2:
+        for p1, p2 in zip(sig1.parameters.values(), sig2.parameters.values()):
+            # Use the first with a default.
+            if p1.default is not _empty or p2.default is not _empty:
+                return idx1 if p1.default is not _empty else idx2
+    # Use the one with a return annotation.
+    return idx1 if ra1 is not None else idx2
+
+
+def _remove_ambiguous_signatures_body(signatures, name=None):
+    # By the sorting of signatures, duplicates will always be adjacent.
+    last_ann = None
+    last_idx = -1
+    to_delete = []
+    found = False
+    for idx, sig in enumerate(signatures):
+        annos = []
+        for param in list(sig.parameters.values()):
+            annos.append(param.annotation)
+        if annos == last_ann:
+            found = True
+            to_delete.append(best_to_remove(signatures, idx, last_idx, name))
+        last_ann = annos
+        last_idx = idx
+
+    if not found:
+        return False, signatures
+    new_sigs = []
+    for idx, sig in enumerate(signatures):
+        if idx not in to_delete:
+            new_sigs.append(sig)
+    return True, new_sigs
+
+
+def is_inconsistent_overload(signatures):
+    count = 0
+    for sig in signatures:
+        count += 1 if "self" in sig.parameters else 0
+    return count != 0 and count != len(signatures)
+
+
+def remove_ambiguous_signatures(signatures, name=None):
+    # This may run more than once because of indexing.
+    found, new_sigs = _remove_ambiguous_signatures_body(signatures)
+    while found:
+        found, new_sigs = _remove_ambiguous_signatures_body(new_sigs)
+    # Python cannot handle mixed methods and classmethods. Remove the latter.
+    if is_inconsistent_overload(signatures):
+        new_sigs = list(sig for sig in new_sigs if "self" in sig.parameters)
+    return new_sigs
 
 
 def create_signature(props, key):
@@ -181,9 +322,15 @@ def create_signature(props, key):
         # empty signatures string
         return
     if isinstance(props["multi"], list):
-        # multi sig: call recursively
-        return list(create_signature(elem, key)
-                    for elem in props["multi"])
+        # multi sig: call recursively.
+        # For debugging: Print the name!
+        # name = props["multi"][0]["fullname"]
+        res = list(create_signature(elem, key) for elem in props["multi"])
+        # PYSIDE-2846: Sort multi-signatures by inheritance in order to avoid shadowing.
+        res = sort_by_inheritance(res)
+        res = remove_ambiguous_signatures(res, name=None)
+        return res if len(res) > 1 else res[0]
+
     if type(key) is tuple:
         _, modifier = key
     else:
@@ -214,9 +361,16 @@ def create_signature(props, key):
         del annotations["return"]
 
     # Build a signature.
-    kind = inspect._POSITIONAL_OR_KEYWORD
+    kind = DEFAULT_PARAM_KIND
     params = []
+    snake_flag = using_snake_case()
+
     for idx, name in enumerate(varnames):
+        if name == "*":
+            # This is a switch.
+            # Important: It must have a default to simplify the calculation.
+            kind = _KEYWORD_ONLY
+            continue
         if name.startswith("**"):
             kind = _VAR_KEYWORD
         elif name.startswith("*"):
@@ -227,16 +381,33 @@ def create_signature(props, key):
         name = name.lstrip("*")
         defpos = idx - len(varnames) + len(defaults)
         default = defaults[defpos] if defpos >= 0 else _empty
+        if default is not _empty:
+            if kind != _KEYWORD_ONLY:
+                kind = _POSITIONAL_OR_KEYWORD
         if default is None:
             ann = typing.Optional[ann]
         if default is not _empty and layout.ellipsis:
             default = ellipsis
+        if kind is _KEYWORD_ONLY:
+            # All these entries are properties. They might have been used already
+            # as normal parameter before and must be omitted here. Fixing that now:
+            if varnames.count(name) > 1:
+                assert varnames.count(name) == 2
+                if snake_flag and name != (new_name := make_snake_case_name(name)):
+                    # Patch this name backwards because it comes earlier as property.
+                    idx = varnames.index(name)
+                    params[idx] = params[idx].replace(name=new_name)
+                continue
+            if snake_flag:
+                name = make_snake_case_name(name)
         param = inspect.Parameter(name, kind, annotation=ann, default=default)
         params.append(param)
-        if kind == _VAR_POSITIONAL:
-            kind = _KEYWORD_ONLY
+
+    ret_anno = annotations.get('return', _empty)
+    if ret_anno is not _empty and props["fullname"] in missing_optional_return:
+        ret_anno = typing.Optional[ret_anno]
     sig = inspect.Signature(params,
-                            return_annotation=annotations.get('return', _empty),
+                            return_annotation=ret_anno,
                             __validate_parameters__=False)
 
     # the special case of nameless parameters
index e54bec75a5dea9f85f32f0b28549906910b09b78..db4f381f50205371449fe4ef2ba879ecb7a079dc 100644 (file)
@@ -1,4 +1,5 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 # this file intentionally left blank
index 5650e2bc148d709fa0e59dde3d4ee81c16fc767f..2f9db0b6e53cc2b49e599601a7ba064f45017d1d 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 enum_sig.py
@@ -14,7 +15,10 @@ by producing a lot of clarity.
 import inspect
 import sys
 import types
+import typing
 from shibokensupport.signature import get_signature as get_sig
+from shibokensupport.signature.layout import DEFAULT_PARAM_KIND
+from enum import Enum
 
 
 """
@@ -40,7 +44,7 @@ def signal_check(thing):
     return thing and type(thing) in (Signal, SignalInstance)
 
 
-class ExactEnumerator(object):
+class ExactEnumerator:
     """
     ExactEnumerator enumerates all signatures in a module as they are.
 
@@ -49,6 +53,20 @@ class ExactEnumerator(object):
     is desired.
     """
 
+    # PYSIDE-2846: This set holds functions with `__i<f>__` != `__<f>__` signature.
+    mypy_aug_ass_errors = set()
+
+    augmented_assignments = {
+        "__iadd__": "__add__",
+        "__isub__": "__sub__",
+        "__imul__": "__mul__",
+        "__idiv__": "__div__",
+    }
+
+    # PYSIDE-2846: Inheritance errors which are in QtDesigner
+    mypy_misc_class_errors = set()
+    mypy_misc_class_errors.add("QPyDesignerPropertySheetExtension")
+
     def __init__(self, formatter, result_type=dict):
         global EnumMeta, Signal, SignalInstance
         try:
@@ -82,6 +100,8 @@ class ExactEnumerator(object):
         with self.fmt.module(mod_name):
             module = sys.modules[mod_name]
             members = inspect.getmembers(module, inspect.isclass)
+            # PYSIDE-2846: Make sure not to get QTextFrame.iterator :(
+            members = list(x for x in members if "." not in x[0])
             functions = inspect.getmembers(module, inspect.isroutine)
             ret = self.result_type()
             self.fmt.class_name = None
@@ -157,7 +177,26 @@ class ExactEnumerator(object):
             if thing_name in self.collision_candidates:
                 self.collision_track.add(thing_name)
 
+        # PYSIDE-2846: Mark inconsistency between __iadd__ and __add__ etc.
+        for aug_ass in self.augmented_assignments:
+            if aug_ass in klass.__dict__:
+                other = self.augmented_assignments[aug_ass]
+                if other in klass.__dict__:
+                    aug_sig = self.get_signature(klass.__dict__[aug_ass])
+                    other_sig = self.get_signature(klass.__dict__[other])
+                    if aug_sig != other_sig:
+                        func = klass.__dict__[aug_ass]
+                        self.mypy_aug_ass_errors.add(func)
+
         init_signature = getattr(klass, "__signature__", None)
+        # PYSIDE-2752: Enums without values will not have a constructor, so
+        # we set the init_signature to None, to avoid having an empty pyi
+        # entry, like:
+        #    class QCborTag(enum.IntEnum):
+        #  or
+        #    class BeginFrameFlag(enum.Flag):
+        if issubclass(klass, Enum):
+            init_signature = None
         # sort by class then enum value
         enums.sort(key=lambda tup: (tup[1], tup[2].value))
 
@@ -169,7 +208,11 @@ class ExactEnumerator(object):
         self.fmt.have_body = bool(subclasses or sigs or properties or enums or  # noqa W:504
                                   init_signature or signals or attributes)
 
-        with self.fmt.klass(class_name, class_str):
+        has_misc_error = class_name in self.mypy_misc_class_errors
+        if issubclass(klass, Enum) and not len(enums):
+            # PYSIDE-2846: We keep the empty enum and ignore the error.
+            has_misc_error = True
+        with self.fmt.klass(class_name, class_str, has_misc_error):
             self.fmt.level += 1
             self.fmt.class_name = class_name
             if hasattr(self.fmt, "enum"):
@@ -227,8 +270,15 @@ class ExactEnumerator(object):
         if decorator in self.collision_track:
             decorator = f"builtins.{decorator}"
         signature = self.get_signature(func, decorator)
+        # PYSIDE-2846: Special cases of signatures which inherit from object.
+        _self = inspect.Parameter("self", DEFAULT_PARAM_KIND)
+        if func_name == "__dir__":
+            signature = inspect.Signature([_self], return_annotation=typing.Iterable[str])
+        elif func_name == "__repr__":
+            signature = inspect.Signature([_self], return_annotation=str)
         if signature is not None:
-            with self.fmt.function(func_name, signature, decorator) as key:
+            aug_ass = func in self.mypy_aug_ass_errors
+            with self.fmt.function(func_name, signature, decorator, aug_ass) as key:
                 ret[key] = signature
         del self.func
         return ret
@@ -266,7 +316,7 @@ class SimplifyingEnumerator(ExactEnumerator):
         ret = self.result_type()
         signature = get_sig(func, 'existence')
         sig = stringify(signature) if signature is not None else None
-        if sig is not None and func_name not in ("next", "__next__", "__div__"):
+        if sig is not None:
             with self.fmt.function(func_name, sig) as key:
                 ret[key] = sig
         return ret
@@ -283,8 +333,8 @@ class HintingEnumerator(ExactEnumerator):
     def __init__(self, *args, **kwds):
         super().__init__(*args, **kwds)
         # We need to provide default signatures for class properties.
-        cls_param = inspect.Parameter("cls", inspect._POSITIONAL_OR_KEYWORD)
-        set_param = inspect.Parameter("arg_1", inspect._POSITIONAL_OR_KEYWORD, annotation=object)
+        cls_param = inspect.Parameter("cls", DEFAULT_PARAM_KIND)
+        set_param = inspect.Parameter("arg_1", DEFAULT_PARAM_KIND, annotation=object)
         self.getter_sig = inspect.Signature([cls_param], return_annotation=object)
         self.setter_sig = inspect.Signature([cls_param, set_param])
         self.deleter_sig = inspect.Signature([cls_param])
index ce12dd6c8d1331f6905c0ec0e652b2beb449bcd0..cff54600562fcb1c863cfc05225a15e8848a7cc1 100644 (file)
@@ -1,6 +1,7 @@
 LICENSE_TEXT = """
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 """
 
 # flake8: noqa E:402
@@ -12,9 +13,9 @@ This script generates .pyi files for arbitrary modules.
 """
 
 import argparse
+import inspect
 import io
 import logging
-import os
 import re
 import sys
 import typing
@@ -26,13 +27,10 @@ from textwrap import dedent
 from shibokensupport.signature.lib.enum_sig import HintingEnumerator
 from shibokensupport.signature.lib.tool import build_brace_pattern
 
-# Can we use forward references?
-USE_PEP563 = sys.version_info[:2] >= (3, 7)
-
 indent = " " * 4
 
 
-class Writer(object):
+class Writer:
     def __init__(self, outfile, *args):
         self.outfile = outfile
         self.history = [True, True]
@@ -59,43 +57,74 @@ class Formatter(Writer):
     The separation in formatter and enumerator is done to keep the
     unrelated tasks of enumeration and formatting apart.
     """
+
     def __init__(self, outfile, options, *args):
-        # XXX Find out which of these patches is still necessary!
         self.options = options
         Writer.__init__(self, outfile, *args)
-        # patching __repr__ to disable the __repr__ of typing.TypeVar:
-        """
-            def __repr__(self):
-                if self.__covariant__:
-                    prefix = '+'
-                elif self.__contravariant__:
-                    prefix = '-'
-                else:
-                    prefix = '~'
-                return prefix + self.__name__
-        """
-        def _typevar__repr__(self):
-            return f"typing.{self.__name__}"
-        # This is no longer necessary for modern typing versions.
-        # Ignore therefore if the repr is read-only and cannot be changed.
-        try:
-            typing.TypeVar.__repr__ = _typevar__repr__
-        except TypeError:
-            pass
-        # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]"
-        # I tried hard to replace typing.Optional by a simple override, but
-        # this became _way_ too much.
-        # See also the comment in layout.py .
-        brace_pat = build_brace_pattern(3, ",")
-        pattern = fr"\b Union \s* \[ \s* {brace_pat} \s*, \s* NoneType \s* \]"
-        replace = r"Optional[\1]"
-        optional_searcher = re.compile(pattern, flags=re.VERBOSE)
-
-        def optional_replacer(source):
-            return optional_searcher.sub(replace, str(source))
-        self.optional_replacer = optional_replacer
-        # self.level is maintained by enum_sig.py
-        # self.is_method() is true for non-plain functions.
+
+    # Re-add the `typing` prefix that inspect would throw away.
+    # We do that by overwriting the relevant part of the function.
+
+    backup = inspect.formatannotation
+
+    @classmethod
+    def formatannotation(cls, annotation, base_module=None):
+        if getattr(annotation, '__module__', None) == 'typing':
+            # do not remove the prefix!
+            return repr(annotation)
+        # do the normal action.
+        return cls.backup(annotation, base_module)
+
+    @classmethod
+    def fix_typing_prefix(cls, signature):
+        # modify the module, format the signature, restore the module.
+        inspect.formatannotation = cls.formatannotation
+        stringized = str(signature)
+        inspect.formatannotation = cls.backup
+        return stringized
+
+    # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]"
+    # I tried hard to replace typing.Optional by a simple override, but
+    # this became _way_ too much.
+    # See also the comment in layout.py .
+
+    # PYSIDE-2786: Since Python 3.9, we can use the "|" notation.
+    #              Transform "Union" and "Optional" this way.
+    brace_pat = build_brace_pattern(3, ",=")
+    opt_uni_searcher = re.compile(fr"""
+            \b                      # edge of a word
+            (typing\.Optional |
+             typing\.Union)         # word to find
+            \s*                     # optional whitespace
+            (?= \[ )                # Lookahead enforces a square bracket
+            {brace_pat}             # braces tower, one capturing brace level
+        """, flags=re.VERBOSE)
+    brace_searcher = re.compile(brace_pat, flags=re.VERBOSE)
+    split = brace_searcher.split
+
+    @classmethod
+    def optional_replacer(cls, source):
+        # PYSIDE-2517: findChild/findChildren type hints:
+        # PlaceHolderType fix to avoid the '~' from TypeVar.__repr__
+        if "~PlaceHolderType" in source:
+            source = source.replace("~PlaceHolderType", "PlaceHolderType")
+
+        while match := cls.opt_uni_searcher.search(source):
+            start = match.start()
+            end = match.end()
+            name = match.group(1)
+            body = match.group(2).strip()[1:-1]
+            # Note: this list is interspersed with "," and surrounded by "", see parser.py
+            parts = [x.strip() for x in cls.split(body) if x.strip() not in ("", ",")]
+            if name == "typing.Optional":
+                parts.append("None")
+            res = " | ".join(parts)
+            source = source[: start] + res + source[end :]
+        # Replace all "NoneType" strings by "None" which is a typing convention.
+        return source.replace("NoneType", "None")
+
+    # self.level is maintained by enum_sig.py
+    # self.is_method() is true for non-plain functions.
 
     def section(self):
         if self.level == 0:
@@ -114,60 +143,72 @@ class Formatter(Writer):
         yield
 
     @contextmanager
-    def klass(self, class_name, class_str):
+    def klass(self, class_name, class_str, has_misc_error=None):
+        err_ignore = "  # type: ignore[misc]"
+        opt_comment = err_ignore if has_misc_error else ""
         spaces = indent * self.level
         while "." in class_name:
             class_name = class_name.split(".", 1)[-1]
             class_str = class_str.split(".", 1)[-1]
         if self.have_body:
-            self.print(f"{spaces}class {class_str}:")
+            self.print(f"{spaces}class {class_str}:{opt_comment}")
         else:
-            self.print(f"{spaces}class {class_str}: ...")
+            self.print(f"{spaces}class {class_str}: ...{opt_comment}")
         yield
 
     @contextmanager
-    def function(self, func_name, signature, decorator=None):
+    def function(self, func_name, signature, decorator=None, aug_ass=None):
         if func_name == "__init__":
             self.print()
         key = func_name
         spaces = indent * self.level
+        err_ignore = "  # type: ignore[misc]"
         if isinstance(signature, list):
+            # PYSIDE-2846: Disable errors in augmented assignments.
+            opt_comment = (err_ignore if aug_ass else "")
             for sig in signature:
-                self.print(f'{spaces}@overload')
+                self.print(f'{spaces}@typing.overload{opt_comment}')
+                opt_comment = ""
                 self._function(func_name, sig, spaces)
         else:
-            self._function(func_name, signature, spaces, decorator)
+            opt_comment = err_ignore if aug_ass else ""
+            self._function(func_name, signature, spaces, decorator, opt_comment)
         if func_name == "__init__":
             self.print()
         yield key
 
-    def _function(self, func_name, signature, spaces, decorator=None):
+    def _function(self, func_name, signature, spaces, decorator=None, opt_comment=""):
         if decorator:
             # In case of a PyClassProperty the classmethod decorator is not used.
             self.print(f'{spaces}@{decorator}')
         elif self.is_method() and "self" not in signature.parameters:
             kind = "class" if "cls" in signature.parameters else "static"
             self.print(f'{spaces}@{kind}method')
+        # the formatting with the inspect module explicitly removes the `typing` prefix.
+        signature = self.fix_typing_prefix(signature)
+        # from now on, the signature will be stringized.
         signature = self.optional_replacer(signature)
-        self.print(f'{spaces}def {func_name}{signature}: ...')
+        self.print(f'{spaces}def {func_name}{signature}: ...{opt_comment}')
 
     @contextmanager
     def enum(self, class_name, enum_name, value):
         spaces = indent * self.level
         hexval = hex(value)
-        self.print(f"{spaces}{enum_name:25}: {class_name} = ... # {hexval}")
+        self.print(f"{spaces}{enum_name:25} = ...  # {hexval if value >= 0 else value}")
         yield
 
     @contextmanager
     def attribute(self, attr_name, attr_value):
         spaces = indent * self.level
-        self.print(f"{spaces}{attr_name:25} = ...  # type: {type(attr_value).__qualname__}")
+        # PYSIDE-2903: Use a fully qualified name in the type comment.
+        full_name = f"{type(attr_value).__module__}.{type(attr_value).__qualname__}"
+        self.print(f"{spaces}{attr_name:25} = ...  # type: {full_name}")
         yield
 
     @contextmanager
     def signal(self, class_name, sig_name, sig_str):
         spaces = indent * self.level
-        self.print(f"{spaces}{sig_name:25}: ClassVar[{class_name}] = ... # {sig_str}")
+        self.print(f"{spaces}{sig_name:25}: typing.ClassVar[{class_name}] = ... # {sig_str}")
         yield
 
 
@@ -179,8 +220,8 @@ FROM_IMPORTS = [
     (None, ["builtins"]),
     (None, ["os"]),
     (None, ["enum"]),
+    (None, ["typing"]),
     ("collections.abc", ["Iterable"]),
-    ("typing", sorted(typing.__all__)),
     ("PySide6.QtCore", ["PyClassProperty", "Signal", "SignalInstance"]),
     ("shiboken6", ["Shiboken"]),
     ]
@@ -246,13 +287,12 @@ def generate_pyi(import_name, outpath, options):
     outfile = io.StringIO()
     fmt = Formatter(outfile, options)
     fmt.print(LICENSE_TEXT.strip())
-    if USE_PEP563:
-        fmt.print("from __future__ import annotations")
-        fmt.print()
     fmt.print(dedent(f'''\
         """
         This file contains the exact signatures for all functions in module
         {import_name}, except for defaults which are replaced by "...".
+
+        # mypy: disable-error-code="override, overload-overlap"
         """
         '''))
     HintingEnumerator(fmt).module(import_name)
@@ -279,7 +319,9 @@ def generate_pyi(import_name, outpath, options):
                         wr.print("import " + imp)
                 wr.print()
                 for mod, imports in filter_from_imports(FROM_IMPORTS, text):
-                    import_args = ', '.join(imports)
+                    # Sorting, and getting uniques to avoid duplications
+                    # on "Iterable" having a couple of entries.
+                    import_args = ', '.join(sorted(set(imports)))
                     if mod is None:
                         # special case, a normal import
                         wr.print(f"import {import_args}")
@@ -287,7 +329,11 @@ def generate_pyi(import_name, outpath, options):
                         wr.print(f"from {mod} import {import_args}")
                 wr.print()
                 wr.print()
-                wr.print("NoneType: TypeAlias = type[None]")
+                # We use it only in QtCore at the moment, but this
+                # could be extended to other modules. (must import QObject then)
+                if import_name == "PySide6.QtCore":
+                    wr.print("PlaceHolderType = typing.TypeVar(\"PlaceHolderType\", "
+                             "bound=PySide6.QtCore.QObject)")
                 wr.print()
             else:
                 wr.print(line)
index 979dcf4ce76ce8cf87070d4e651cd64bfcc4d6af..bc56cb922e50728b83b26f85d294164e634b466a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 tool.py
@@ -36,6 +37,8 @@ def build_brace_pattern(level, separators):
     There are other regex engines available which allow recursive patterns,
     avoiding this problem completely. It might be considered to switch to
     such an engine if the external module is not a problem.
+
+    Note: This pattern has exactly one capturing group.
     """
     assert type(separators) is str
 
index fb4c9eecac9bf744a93762946985191bfec62349..fc85fc00ee9fe45c9c629de159387b118db52f8a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 # flake8: noqa E:402
 # flake8: noqa F:401
@@ -74,6 +75,7 @@ import builtins
 import signature_bootstrap
 from shibokensupport import signature, feature
 signature.get_signature = signature_bootstrap.get_signature
+signature.make_snake_case_name = signature_bootstrap.make_snake_case_name
 # PYSIDE-1019: Publish the __feature__ dictionary.
 feature.pyside_feature_dict = signature_bootstrap.pyside_feature_dict
 builtins.__feature_import__ = signature_bootstrap.__feature_import__
index 944a928e688ad5b8316b6f2e610dbd492fc8e6b1..af0fc6c0ae30405f1e14062b04bd3042ba23f888 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 # flake8: noqa E:203
 
@@ -23,20 +24,31 @@ from typing import TypeVar, Generic
 from _imp import is_builtin
 
 
-class ellipsis(object):
+class ellipsis:
     def __repr__(self):
         return "..."
 
 
+if not hasattr(typing, "Self"):
+    @typing._SpecialForm
+    def Self(self, parameters):
+        raise TypeError(f"{self} is not subscriptable")
+    typing.Self = Self
+
 ellipsis = ellipsis()
 Point = typing.Tuple[int, int]
 Variant = typing.Any
-QImageCleanupFunction = typing.Callable
+QImageCleanupFunction = typing.Callable[..., typing.Any]
 
 # unfortunately, typing.Optional[t] expands to typing.Union[t, NoneType]
 # Until we can force it to create Optional[t] again, we use this.
 NoneType = type(None)
 
+# PYSIDE-2517: findChild/findChildren type hints:
+# Placeholder so it does not trigger an UNDEFINED error while building.
+# Later it will be bound to a QObject, within the QtCore types extensions
+PlaceHolderType = TypeVar("PlaceHolderType")
+
 _S = TypeVar("_S")
 
 MultiMap = typing.DefaultDict[str, typing.List[str]]
@@ -79,12 +91,7 @@ class _NotCalled(str):
         return eval(text, namespace)
 
 
-USE_PEP563 = False
-# Note: we cannot know if this feature has been imported.
-# Otherwise it would be "sys.version_info[:2] >= (3, 7)".
-# We *can* eventually inspect sys.modules and look if
-# the calling module has this future statement set,
-# but should we do that?
+USE_PEP563 = sys.version_info[:2] >= (3, 10)
 
 
 # Some types are abstract. They just show their name.
@@ -114,8 +121,12 @@ class Instance(_NotCalled):
     pass
 
 
+class KeywordOnly(_NotCalled):
+    pass
+
+
 # Parameterized primitive variables
-class _Parameterized(object):
+class _Parameterized:
     def __init__(self, type):
         self.type = type
         self.__name__ = self.__class__.__name__
@@ -138,7 +149,7 @@ class ArrayLikeVariable(_Parameterized):
 StringList = ArrayLikeVariable(str)
 
 
-class Reloader(object):
+class Reloader:
     """
     Reloder class
 
@@ -181,6 +192,10 @@ class Reloader(object):
                 # Modules are in place, we can update the type_map.
                 g.update(g.pop(proc_name)())
 
+            # Also record an efficient list of modules for PySide.
+            if mod_name.startswith("PySide6."):
+                pyside_modules.add(mod_name)
+
 
 def check_module(mod):
     # During a build, there exist the modules already as directories,
@@ -194,7 +209,9 @@ def check_module(mod):
 
 update_mapping = Reloader().update
 type_map = {}
+type_map_tuple = {}
 namespace = globals()  # our module's __dict__
+pyside_modules: set[str] = set()
 
 type_map.update({
     "...": ellipsis,
@@ -206,13 +223,14 @@ type_map.update({
     "int": int,
     "List": ArrayLikeVariable,
     "Optional": typing.Optional,
+    "Iterable": typing.Iterable,
     "long": int,
     "long long": int,
     "nullptr": None,
-    "PyCallable": typing.Callable,
+    "PyCallable": typing.Callable[..., typing.Any],
     "PyObject": object,
     "PyObject*": object,
-    "PyArrayObject": ArrayLikeVariable, # numpy
+    "PyArrayObject": ArrayLikeVariable(typing.Any),  # numpy
     "PyPathLike": typing.Union[str, bytes, os.PathLike[str]],
     "PySequence": typing.Iterable,  # important for numpy
     "PyTypeObject": type,
@@ -290,18 +308,10 @@ type_map.update({
     "ushort": int,
     "void": int,  # be more specific?
     "WId": WId,
-    "zero(bytes)": b"",
-    "zero(Char)": 0,
-    "zero(float)": 0,
-    "zero(int)": 0,
-    "zero(object)": None,
-    "zero(str)": "",
-    "zero(typing.Any)": None,
-    "zero(Any)": None,
     # This can be refined by importing numpy.typing optionally, but better than nothing.
     "numpy.ndarray": typing.List[typing.Any],
     "std.array[int, 4]": typing.List[int],
-    "std.array[float, 4]": typing.List[float]
+    "std.array[float, 4]": typing.List[float],
 })
 
 type_map.update({
@@ -336,6 +346,7 @@ type_map.update({
     "quint8*"       : bytearray,  # only for QCborStreamReader and QCborValue
     "uchar*"        : typing.Union[bytes, bytearray, memoryview],
     "unsigned char*": typing.Union[bytes, bytearray, memoryview],
+    "char*const"    : typing.Union[bytes, bytearray, memoryview],
 })
 
 type_map.update({
@@ -378,8 +389,12 @@ type_map.update({
 # PYSIDE-1538: We need to treat "std::optional" accordingly.
 type_map.update({
     "std.optional": typing.Optional,
-    })
+})
 
+# PYSIDE-2846: A special keyord only switching token.
+type_map.update({
+    "KeywordOnly": KeywordOnly("None"),
+})
 
 # The Shiboken Part
 def init_Shiboken():
@@ -462,15 +477,10 @@ def init_smart():
 
 # The PySide Part
 def init_PySide6_QtCore():
-    from PySide6.QtCore import Qt, QUrl, QDir, QKeyCombination
-    from PySide6.QtCore import QRect, QRectF, QSize, QPoint, QLocale, QByteArray
+    from PySide6.QtCore import Qt, QUrl, QDir, QByteArray
+    from PySide6.QtCore import QRect, QRectF, QSize, QPoint
     from PySide6.QtCore import QMarginsF  # 5.9
-    from PySide6.QtCore import SignalInstance
-    try:
-        # seems to be not generated by 5.9 ATM.
-        from PySide6.QtCore import Connection
-    except ImportError:
-        pass
+
     type_map.update({
         "' '": " ",
         "'%'": "%",
@@ -485,22 +495,21 @@ def init_PySide6_QtCore():
         "size_t": int,
         "NULL": None,  # 5.6, MSVC
         "nullptr": None,  # 5.9
+        # PYSIDE-2517: findChild/findChildren type hints:
+        "PlaceHolderType": typing.TypeVar("PlaceHolderType", bound=PySide6.QtCore.QObject),
         "PyBuffer": typing.Union[bytes, bytearray, memoryview],
         "PyByteArray": bytearray,
         "PyBytes": typing.Union[bytes, bytearray, memoryview],
         "PyTuple": typing.Tuple,
+        "QDeadlineTimer.Forever": PySide6.QtCore.QDeadlineTimer.ForeverConstant.Forever,
         "QDeadlineTimer(QDeadlineTimer.Forever)": Instance("PySide6.QtCore.QDeadlineTimer"),
         "PySide6.QtCore.QUrl.ComponentFormattingOptions":
             PySide6.QtCore.QUrl.ComponentFormattingOption,  # mismatch option/enum, why???
         "PyUnicode": typing.Text,
-        "QByteArrayView": QByteArray,
+        "QByteArrayView": PySide6.QtCore.QByteArray,
         "Q_NULLPTR": None,
         "QCalendar.Unspecified": PySide6.QtCore.QCalendar.Unspecified,
         "QCborTag(-1)": ulong_max,
-        "QDir.Filters(AllEntries | NoDotAndDotDot)": Instance(
-            "QDir.Filters(QDir.AllEntries | QDir.NoDotAndDotDot)"),
-        "QDir.SortFlags(Name | IgnoreCase)": Instance(
-            "QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"),
         "QEvent.Type.None": None,
         "QGenericArgument((0))": ellipsis,  # 5.6, RHEL 6.6. Is that ok?
         "QGenericArgument()": ellipsis,
@@ -511,32 +520,29 @@ def init_PySide6_QtCore():
         "QJsonObject": typing.Dict[str, PySide6.QtCore.QJsonValue],
         "QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"),  # repr is btw. very wrong, fix it?!
         "QModelIndexList": typing.List[PySide6.QtCore.QModelIndex],
-        "PySideSignalInstance": SignalInstance,
+        "PySideSignalInstance": PySide6.QtCore.SignalInstance,
         "QString()": "",
         "Flag.Default": Instance("PySide6.QtCore.QStringConverterBase.Flags"),
         "QStringList()": [],
         "QStringRef": str,
         "QStringRef": str,
         "Qt.HANDLE": int,  # be more explicit with some constants?
-        "QUrl.FormattingOptions(PrettyDecoded)": Instance(
-            "QUrl.FormattingOptions(QUrl.PrettyDecoded)"),
+        "QUrl.FormattingOptions(QUrl.ComponentFormattingOption.PrettyDecoded)":
+            PySide6.QtCore.QUrl.ComponentFormattingOption.PrettyDecoded,
         "QVariant()": Invalid(Variant),
         "QVariant.Type": type,  # not so sure here...
         "QVariantMap": typing.Dict[str, Variant],
         "std.chrono.seconds{5}" : ellipsis,
     })
-    try:
+    from shibokensupport.signature.parser import using_snake_case
+    if using_snake_case():
         type_map.update({
-            "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection,  # wrong!
+            "QKeyCombination.fromCombined(0)": PySide6.QtCore.QKeyCombination.from_combined(0),
         })
-    except AttributeError:
-        # this does not exist on 5.9 ATM.
-        pass
-
     # special case - char* can either be 'bytes' or 'str'. The default is 'bytes'.
     # Here we manually set it to map to 'str'.
-    type_map.update({("PySide6.QtCore.QObject.setProperty", "char*"): str})
-    type_map.update({("PySide6.QtCore.QObject.property", "char*"): str})
+    type_map_tuple.update({("PySide6.QtCore.QObject.setProperty", "char*"): str})
+    type_map_tuple.update({("PySide6.QtCore.QObject.property", "char*"): str})
 
     return locals()
 
@@ -573,7 +579,7 @@ def init_PySide6_QtGui():
 
     # special case - char* can either be 'bytes' or 'str'. The default is 'bytes'.
     # Here we manually set it to map to 'str'.
-    type_map.update({("PySide6.QtGui.QPixmap.save", "char*"): str})
+    type_map_tuple.update({("PySide6.QtGui.QPixmap.save", "char*"): str})
 
     return locals()
 
@@ -650,9 +656,7 @@ def init_PySide6_QtQuick():
 
 
 def init_PySide6_QtTest():
-    from PySide6.QtCore import SignalInstance
     type_map.update({
-        "PySideSignalInstance": SignalInstance,
         "PySide6.QtTest.QTest.PySideQTouchEventSequence": PySide6.QtTest.QTest.QTouchEventSequence,
         "PySide6.QtTest.QTouchEventSequence": PySide6.QtTest.QTest.QTouchEventSequence,
     })
@@ -716,4 +720,38 @@ def init_testbinding():
     })
     return locals()
 
+
+# Functions which should return Optional(result) but don't.
+missing_optional_return = {
+    "PySide6.QtWidgets.QApplication.activeModalWidget",
+    "PySide6.QtWidgets.QApplication.activePopupWidget",
+    "PySide6.QtWidgets.QApplication.activeWindow",
+    "PySide6.QtWidgets.QApplication.focusWidget",
+    "PySide6.QtWidgets.QApplication.setStyle",
+    "PySide6.QtWidgets.QApplication.topLevelAt",
+    "PySide6.QtWidgets.QApplication.widgetAt",
+    "PySide6.QtWidgets.QComboBox.completer",
+    "PySide6.QtWidgets.QComboBox.lineEdit",
+    "PySide6.QtWidgets.QComboBox.validator",
+    "PySide6.QtWidgets.QGridLayout.itemAt",
+    "PySide6.QtWidgets.QGridLayout.itemAtPosition",
+    "PySide6.QtWidgets.QLayout.itemAt",
+    "PySide6.QtWidgets.QTableWidget.horizontalHeaderItem",
+    "PySide6.QtWidgets.QTableWidget.item",
+    "PySide6.QtWidgets.QTableWidget.itemAt",
+    "PySide6.QtWidgets.QTableWidget.mimeData",
+    "PySide6.QtWidgets.QWidget.childAt",
+    "PySide6.QtWidgets.QWidget.find",
+    "PySide6.QtWidgets.QWidget.focusProxy",
+    "PySide6.QtWidgets.QWidget.graphicsEffect",
+    "PySide6.QtWidgets.QWidget.graphicsProxyWidget",
+    "PySide6.QtWidgets.QWidget.keyboardGrabber",
+    "PySide6.QtWidgets.QWidget.layout",
+    "PySide6.QtWidgets.QWidget.mouseGrabber",
+    "PySide6.QtWidgets.QWidget.nativeParentWidget",
+    "PySide6.QtWidgets.QWidget.nextInFocusChain",
+    "PySide6.QtWidgets.QWidget.parentWidget",
+    "PySide6.QtWidgets.QWidget.previousInFocusChain",
+}
+
 # end of file
index 9b48ab44291f2f489c5d2a1ad0404271826c3f9b..0f582fe24ae7d72487a800c1b6784448773aa9e1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import ast
 import enum
@@ -11,9 +12,10 @@ import typing
 import warnings
 
 from types import SimpleNamespace
-from shibokensupport.signature.mapping import (type_map, update_mapping,
-    namespace, _NotCalled, ResultVariable, ArrayLikeVariable)  # noqa E:128
+from shibokensupport.signature.mapping import (type_map, type_map_tuple, update_mapping,
+    namespace, _NotCalled, ResultVariable, ArrayLikeVariable, pyside_modules)  # noqa E:128
 from shibokensupport.signature.lib.tool import build_brace_pattern
+from shibokensupport.signature import make_snake_case_name
 
 _DEBUG = False
 LIST_KEYWORDS = False
@@ -167,8 +169,9 @@ def _parse_line(line):
     return vars(ret)
 
 
-def _using_snake_case():
+def using_snake_case():
     # Note that this function should stay here where we use snake_case.
+    # This function is only meant for creating correct PYI files.
     if "PySide6.QtCore" not in sys.modules:
         return False
     from PySide6.QtCore import QDir
@@ -190,24 +193,13 @@ def _handle_instance_fixup(thing):
         return thing
     start, stop = match.start(), match.end() - 1
     pre, func, args = thing[:start], thing[start:stop], thing[stop:]
-    if func[0].isupper() or func.startswith("gl") and func[2:3].isupper():
-        return thing
-    # Now convert this string to snake case.
-    snake_func = ""
-    for idx, char in enumerate(func):
-        if char.isupper():
-            if idx and func[idx - 1].isupper():
-                # two upper chars are forbidden
-                return thing
-            snake_func += f"_{char.lower()}"
-        else:
-            snake_func += char
+    snake_func = make_snake_case_name(func)
     return f"{pre}{snake_func}{args}"
 
 
 def make_good_value(thing, valtype):
     # PYSIDE-1019: Handle instance calls (which are really seldom)
-    if "(" in thing and _using_snake_case():
+    if "(" in thing and using_snake_case():
         thing = _handle_instance_fixup(thing)
     try:
         if thing.endswith("()"):
@@ -253,10 +245,11 @@ def _resolve_value(thing, valtype, line):
     if thing in ("0", "None") and valtype:
         if valtype.startswith("PySide6.") or valtype.startswith("typing."):
             return None
-        map = type_map[valtype]
+        mapped = type_map.get(valtype)
         # typing.Any: '_SpecialForm' object has no attribute '__name__'
-        name = get_name(map) if hasattr(map, "__name__") else str(map)
+        name = get_name(mapped) if hasattr(mapped, "__name__") else str(mapped)
         thing = f"zero({name})"
+        type_map[f"zero({name})"] = None
     if thing in type_map:
         return type_map[thing]
     res = make_good_value(thing, valtype)
@@ -267,6 +260,9 @@ def _resolve_value(thing, valtype, line):
     if res is not None:
         type_map[thing] = res
         return res
+    # Still not found. Look into the imported modules.
+    if res := get_from_another_module(thing):
+        return res
     warnings.warn(f"""pyside_type_init:_resolve_value
 
         UNRECOGNIZED:   {thing!r}
@@ -296,6 +292,11 @@ def to_string(thing):
     # i.e. it must be an idempotent mapping.
     if isinstance(thing, str):
         return thing
+    # PYSIDE-2517: findChild/findChildren type hints:
+    # TypeVar doesn't have a __qualname__ attribute,
+    # so we fall back to use __name__ before the next condition.
+    if isinstance(thing, typing.TypeVar):
+        return get_name(thing)
     if hasattr(thing, "__name__") and thing.__module__ != "typing":
         m = thing.__module__
         dot = "." in str(thing) or m not in (thing.__qualname__, "builtins")
@@ -317,12 +318,27 @@ def handle_matrix(arg):
     return eval(result, globals(), namespace)
 
 
+def get_from_another_module(thing):
+    top = thing.split(".", 1)[0] if "." in thing else thing
+    for mod_name in pyside_modules:
+        mod = sys.modules[mod_name]
+        if hasattr(mod, top):
+            try:
+                res = eval(f"{mod_name}.{thing}", globals(), namespace)
+                type_map[thing] = res
+                return res
+            except AttributeError:
+                # Maybe it was anothr module...
+                pass
+    return None
+
+
 def _resolve_type(thing, line, level, var_handler, func_name=None):
     # manual set of 'str' instead of 'bytes'
     if func_name:
         new_thing = (func_name, thing)
-        if new_thing in type_map:
-            return type_map[new_thing]
+        if new_thing in type_map_tuple:
+            return type_map_tuple[new_thing]
 
     # Capture total replacements, first. Happens in
     # "PySide6.QtCore.QCborStreamReader.StringResult[PySide6.QtCore.QByteArray]"
@@ -331,6 +347,9 @@ def _resolve_type(thing, line, level, var_handler, func_name=None):
 
     # Now the nested structures are handled.
     if "[" in thing:
+        # Special case: Callable[[],
+        if thing == "[]":
+            return thing
         # handle primitive arrays
         if re.search(r"\[\d*\]$", thing):
             thing = _resolve_arraytype(thing, line)
@@ -404,6 +423,11 @@ def handle_retvar(obj):
     return _handle_generic(obj, typing.List)
 
 
+class Hashabledict(dict):
+    def __hash__(self):
+        return hash(frozenset(self))
+
+
 def calculate_props(line):
     parsed = SimpleNamespace(**_parse_line(line.strip()))
     arglist = parsed.arglist
@@ -420,6 +444,10 @@ def calculate_props(line):
         annotations[name] = _resolve_type(ann, line, 0, handle_argvar, parsed.funcname)
         if len(tup) == 3:
             default = _resolve_value(tup[2], ann, line)
+            # PYSIDE-2846: When creating signatures, the defaults should be hashable.
+            #              For that to work, we use `Hashabledict`.
+            if type(default) is dict:
+                default = Hashabledict(default)
             _defaults.append(default)
     defaults = tuple(_defaults)
     returntype = parsed.returntype
@@ -438,6 +466,7 @@ def calculate_props(line):
     funcname = parsed.funcname
     shortname = funcname[funcname.rindex(".") + 1:]
     props.name = shortname
+    props.fullname = funcname
     props.multi = parsed.multi
     fix_variables(props, line)
     return vars(props)
@@ -495,44 +524,13 @@ def fix_variables(props, line):
     props.defaults = tuple(defaults)
 
 
-def fixup_multilines(lines):
-    """
-    Multilines can collapse when certain distinctions between C++ types
-    vanish after mapping to Python.
-    This function fixes this by re-computing multiline-ness.
-    """
-    res = []
-    multi_lines = []
-    for line in lines:
-        multi = re.match(r"([0-9]+):", line)
-        if multi:
-            idx, rest = int(multi.group(1)), line[multi.end():]
-            multi_lines.append(rest)
-            if idx > 0:
-                continue
-            # remove duplicates
-            multi_lines = sorted(set(multi_lines))
-            # renumber or return a single line
-            nmulti = len(multi_lines)
-            if nmulti > 1:
-                for idx, line in enumerate(multi_lines):
-                    res.append(f"{nmulti-idx-1}:{line}")
-            else:
-                res.append(multi_lines[0])
-            multi_lines = []
-        else:
-            res.append(line)
-    return res
-
-
 def pyside_type_init(type_key, sig_strings):
     dprint()
     dprint(f"Initialization of type key '{type_key}'")
     update_mapping()
-    lines = fixup_multilines(sig_strings)
     ret = {}
     multi_props = []
-    for line in lines:
+    for line in sig_strings:
         props = calculate_props(line)
         shortname = props["name"]
         multi = props["multi"]
index b3adfe78bd651d4dbc541d5781fd7eb1f89b5185..7eb7e37f413db38b8ae39b06e647784071fa104c 100644 (file)
@@ -26,7 +26,7 @@ if (Shiboken::Object::checkType(%1)) {
     std::vector<void*> ptrs = Shiboken::Object::cppPointers(reinterpret_cast<SbkObject *>(%1));
     %PYARG_0 = PyTuple_New(ptrs.size());
     for (std::size_t i = 0; i < ptrs.size(); ++i)
-        PyTuple_SET_ITEM(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i]));
+        PyTuple_SetItem(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i]));
 } else {
     PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
 }
@@ -75,6 +75,11 @@ if (!Shiboken::Object::checkType(%1)) {
 }
 // @snippet dump
 
+// @snippet replacemoduledict
+const bool ok = Shiboken::Module::replaceModuleDict(%1, %2, %3);
+%PYARG_0 = %CONVERTTOPYTHON[bool](ok);
+// @snippet replacemoduledict
+
 // @snippet getallvalidwrappers
 const auto setAll = Shiboken::BindingManager::instance().getAllPyObjects();
 PyObject* listAll = PyList_New(0);
@@ -100,14 +105,18 @@ const bool ok = Shiboken::BindingManager::instance().dumpTypeGraph(%1);
 Shiboken::BindingManager::instance().dumpWrapperMap();
 // @snippet dumpwrappermap
 
+// @snippet dumpconverters
+Shiboken::Conversions::dumpConverters();
+// @snippet dumpconverters
+
 // @snippet init
 // Add __version__ and __version_info__ attributes to the module
 PyObject* version = PyTuple_New(5);
-PyTuple_SET_ITEM(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION));
-PyTuple_SET_ITEM(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION));
-PyTuple_SET_ITEM(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION));
-PyTuple_SET_ITEM(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL));
-PyTuple_SET_ITEM(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL));
+PyTuple_SetItem(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION));
+PyTuple_SetItem(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION));
+PyTuple_SetItem(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION));
+PyTuple_SetItem(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL));
+PyTuple_SetItem(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL));
 PyModule_AddObject(module, "__version_info__", version);
 PyModule_AddStringConstant(module, "__version__", SHIBOKEN_VERSION);
 VoidPtr::addVoidPtrToModule(module);
index aa08a8bbfbf92ff812d01492c7cb7bcc109dce94..851f4ef6156950f65114c93b94f018d226d63080 100644 (file)
         <inject-code file="shibokenmodule.cpp" snippet="disassembleframe"/>
     </add-function>
 
+    <add-function signature="replaceModuleDict(PyObject*, PyObject*, PyObject*)" return-type="bool">
+        <inject-code file="shibokenmodule.cpp" snippet="replacemoduledict"/>
+    </add-function>
+
     <add-function signature="dump(PyObject*)" return-type="const char *">
         <inject-code file="shibokenmodule.cpp" snippet="dump"/>
     </add-function>
         <inject-code file="shibokenmodule.cpp" snippet="dumpwrappermap"/>
     </add-function>
 
+    <add-function signature="dumpConverters()">
+        <inject-code file="shibokenmodule.cpp" snippet="dumpconverters"/>
+    </add-function>
+
     <extra-includes>
         <include file-name="sbkversion.h" location="local"/>
         <include file-name="voidptr.h" location="local"/>
+        <include file-name="sbkconverter_p.h" location="local"/>
     </extra-includes>
      <inject-code position="end" file="shibokenmodule.cpp" snippet="init"/>
 </typesystem>
index 4a10f96bfc055f3797e86a88b4f8442a2ab31799..e1d7dda384d78149726e0ca9e4870db99fb00758 100644 (file)
@@ -18,4 +18,3 @@ add_library(libminimal SHARED ${libminimal_SRC})
 target_include_directories(libminimal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 target_compile_definitions(libminimal PRIVATE LIBMINIMAL_BUILD)
 set_property(TARGET libminimal PROPERTY PREFIX "")
-
index 9904ef27db3b8bd7c5aa2456f9bc923baee22b71..281851088ebe9c84582390dfa15d83f5faa25e1a 100644 (file)
@@ -69,4 +69,3 @@ struct LIBMINIMAL_API ListUser
 };
 
 #endif // LISTUSER_H
-
index a63a9c3c9fde7e678d60cb58538f7577fd005f76..9de87cd3ced98d7f6e16a747f88889188a2ddda1 100644 (file)
@@ -13,4 +13,3 @@ bool Obj::virtualMethod(int val)
 {
     return !bool(val%2);
 }
-
index be0bfb52b8defe971923285a9535ad6558a8af99..10cf54e713516fa7c7262fb359fa2ea0d7ee9ec7 100644 (file)
@@ -31,4 +31,3 @@ private:
 };
 
 #endif // OBJ_H
-
index 50f090a7d0bc87cc5f3828dec4f8c43082873ba1..c989cab7b2ab7df13f3d97af537a1aabffea9aec 100644 (file)
@@ -33,4 +33,3 @@ private:
 };
 
 #endif // VAL_H
-
index 0379d740b67fbeec5dda58ecaa47dc6eb0b218ea..ee8e285f25b1fa9b89ec5c398b900fd632957395 100644 (file)
@@ -19,5 +19,3 @@ target_include_directories(libother PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 target_compile_definitions(libother PRIVATE LIBOTHER_BUILD)
 target_link_libraries(libother PUBLIC libsample libsmart)
 set_property(TARGET libother PROPERTY PREFIX "")
-
-
index 6165a6813418a89448c0282e56b4ba060043194d..9f90c43a760f66e16bf2bc9b09064bf73743df9c 100644 (file)
@@ -11,7 +11,7 @@
 
 class ObjectType;
 
-class LIBOTHER_API OtherMultipleDerived : public MDerived1
+class LIBOTHER_API OtherMultipleDerived : public OtherBase, public MDerived1
 {
 public:
     // this will use CppCopier from other module (bug#142)
index 926972340517c644958d3a7a54aa390aba4aee7f..ac0ef05119dbee47af8a58f70e162f905b40e2ae 100644 (file)
@@ -92,4 +92,3 @@ add_library(libsample SHARED ${libsample_SRC})
 target_include_directories(libsample PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 target_compile_definitions(libsample PRIVATE LIBSAMPLE_BUILD)
 set_property(TARGET libsample PROPERTY PREFIX "")
-
index 26766847a0607c67de221cf7ff145a2ceb9eaf97..60e90d7b829d483b98c5f6bd47253b2c8ab09fd8 100644 (file)
@@ -34,4 +34,3 @@ private:
 LIBSAMPLE_API Collector &operator<<(Collector &, const IntWrapper &);
 
 #endif // COLLECTOR_H
-
index 168fe5c449f22df66a1b6ebb044be680624abfa9..1fa6b33050153be04b9148a4263dbfcf7064ed39 100644 (file)
@@ -29,4 +29,3 @@ private:
 };
 
 #endif // COMPLEX_H
-
index cf95cb601eae40cadcdf2e5eb1f7013d0e2b3f2d..6ed58ee28b3ef6968e1da8cfe0d96d4cb99eea74 100644 (file)
@@ -69,4 +69,3 @@ private:
     void pureVirtualPrivate() override;
 };
 #endif // DERIVED_H
-
index d82d38eb89285642ab01de4d105daf064a36aeb5..61d57f1c3a0a2d00aca7174f7e00aeedadf7d54b 100644 (file)
@@ -66,5 +66,3 @@ private:
 LIBSAMPLE_API Intersection operator&(const Intersection &a, const Intersection &b);
 
 #endif // FILTER_H
-
-
index 74046dad5f68b07241605225fe92a332919caefa..2d3db9c7c2091013701379fc4732e291ce938cf0 100644 (file)
@@ -41,4 +41,3 @@ private:
 };
 
 #endif // INJECTCODE_H
-
index 96781ed160c2b9466efccc91b4a5c0092c1bdcdd..69559cde51afb1876c9980ee8813fc4e2189169c 100644 (file)
@@ -50,4 +50,3 @@ private:
 };
 
 #endif // LISTUSER_H
-
index 1b44642ae18af05eb5a7e786c5afd47ed7b3dfb6..4c4d309fd91dee5c322fb0fcf630f90ed21e035d 100644 (file)
@@ -206,4 +206,3 @@ main(int argv, char **argc)
 
     return 0;
 }
-
index c39c97738ee8436960fdf2feba03ecd92bac0410..1536813100ac5c215d61463d8c3b3dd49f8429f1 100644 (file)
@@ -12,5 +12,3 @@ int ModifiedConstructor::retrieveValue() const
 {
     return m_stored_value;
 }
-
-
index a27899f3fde3f7d73a366373d929aac5970c24ad..bf5ee2c6871877256943933ef0df761fda6c5988 100644 (file)
@@ -18,4 +18,3 @@ private:
 };
 
 #endif // MODIFIEDCONSTRUCTOR_H
-
index a0b91380b21544ca53fc384e94acc4aab45f4912..995189e969600c38c7f4133d4071426a386b094b 100644 (file)
@@ -24,4 +24,3 @@ private:
 };
 
 #endif // NOIMPLICITCONVERSION_H
-
index 95f0cffd6edc35bd6b9cd6d194bc22a297f4ba7d..7de0d11ac42dfdba2547843207f476bb724c2e39 100644 (file)
@@ -20,4 +20,3 @@ add_library(libsmart SHARED ${libsmart_SRC})
 target_include_directories(libsmart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 target_compile_definitions(libsmart PRIVATE LIBSMART_BUILD)
 set_property(TARGET libsmart PROPERTY PREFIX "")
-
index 1be93db667bd97612e5eb202b69558144af76ed4..472f807f200a96936bb9ec026365ee043b4e9777 100644 (file)
@@ -51,6 +51,26 @@ void StdSharedPtrTestBench::printInt(const std::shared_ptr<int> &p)
     std::cerr << '\n';
 }
 
+std::shared_ptr<double> StdSharedPtrTestBench::createDouble(double v)
+{
+     return std::make_shared<double>(v);
+}
+
+std::shared_ptr<double> StdSharedPtrTestBench::createNullDouble()
+{
+    return {};
+}
+
+void StdSharedPtrTestBench::printDouble(const std::shared_ptr<double> &p)
+{
+    std::cerr << __FUNCTION__ << ' ';
+    if (p.get())
+        std::cerr << *p;
+    else
+        std::cerr << "nullptr";
+    std::cerr << '\n';
+}
+
 std::shared_ptr<std::string> StdSharedPtrTestBench::createString(const char *text)
 {
     return std::make_shared<std::string>(text);
index 04d75d5ef8b4fdfbcf0eaf7c6a68c5f8f9466352..9d4c207b57fc223e9dad24b944b9932aa7744276 100644 (file)
@@ -25,6 +25,10 @@ public:
     static std::shared_ptr<int> createNullInt();
     static void printInt(const std::shared_ptr<int> &);
 
+    static std::shared_ptr<double> createDouble(double v = 42);
+    static std::shared_ptr<double> createNullDouble();
+    static void printDouble(const std::shared_ptr<double> &);
+
     static std::shared_ptr<std::string> createString(const char *text);
     static std::shared_ptr<std::string> createNullString();
     static void printString(const std::shared_ptr<std::string> &);
index 7f132bd349d1e9cad4270bee8019ac40b2d3b90e..6eaae8182bf5abee3909a7b0ceec15a5af9a59a5 100644 (file)
@@ -21,6 +21,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/minimal-binding.txt.in"
                "${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt" @ONLY)
 
 shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+    SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
 
 add_custom_command(
     OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
@@ -29,6 +32,7 @@ add_custom_command(
         ${tool_wrapper}
         $<TARGET_FILE:Shiboken6::shiboken6>
         --project-file=${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt
+        ${UNOPTIMIZE}
         ${GENERATOR_EXTRA_FLAGS}
     DEPENDS ${minimal_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
index 946a869dbc78ae6692ce6fa5fbd597604fd80a86..a45c6edb2351418dd3bdc8f4b11ae1eefa689852 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import re
index 25d683957fec291fb610bbeeb514f86f4ccc34dd..cda4f32b27d9d88945c0b57d18ea2786e956e1ed 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b30bb653aa5e5abc1b844199b7e73f5dce1580a4..bf09eeffbe6191517b4400ea24c16a63af0324df 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 from functools import reduce
 import os
index d9ce0eac0b5f1136849e55d726c9302e8e7e0cdb..0f033e4d1d34280e2ef89909c34bd5f4da0436bb 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index e873845dec4e94a3b1a20bdb0cc48889148a6352..82dc89d3818365d44b4d33903cd6e163d939f514 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 6db6aa616a9821e8821ccdb9b1e838c2aa3f327e..4239fe9a254facae3a0eac100dbfe2e55e43fae5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index c2fc8fc120d1b75788b4eb6f6fa6127e2bc10bc6..c69a1c3c5a274e09314e01502bac123c0a6d5945 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b8225a24712adf3b99910d076ab6f82f62d905e8..b84bb1eb72e9030788d7a16d58528a179b599668 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -57,8 +58,8 @@ class ValTest(unittest.TestCase):
 
     def testPassAndReceiveEnumValue(self):
         val = Val(0)
-        self.assertEqual(val.oneOrTheOtherEnumValue(Val.One), Val.Other)
-        self.assertEqual(val.oneOrTheOtherEnumValue(Val.Other), Val.One)
+        self.assertEqual(val.oneOrTheOtherEnumValue(Val.ValEnum.One), Val.ValEnum.Other)
+        self.assertEqual(val.oneOrTheOtherEnumValue(Val.ValEnum.Other), Val.ValEnum.One)
 
     def testPassValueTypeFromExtendedClass(self):
         val = ExtVal(0)
index 2172593d334d259e566bff005b6cb3d861b28182..2fbc5996e7b2bc99a98875529f4e44865dd2deb7 100644 (file)
@@ -26,6 +26,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/other-binding.txt.in"
                "${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt" @ONLY)
 
 shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+    SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
 
 add_custom_command(
     OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
@@ -34,6 +37,7 @@ add_custom_command(
         ${tool_wrapper}
         $<TARGET_FILE:Shiboken6::shiboken6>
         --project-file=${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt
+        ${UNOPTIMIZE}
         ${GENERATOR_EXTRA_FLAGS}
     DEPENDS ${other_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
@@ -55,4 +59,3 @@ endif()
 
 add_dependencies(other sample smart)
 create_generator_target(other)
-
index 2ba21653dd59b6deb782844466613e85e2a9941c..7819d1e0d20fad24fc2474b74f67e24d2c46117c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Collector shift operators defined in other modules.'''
 
index bd00b58922917a945f104c6177670949caa3390e..e837300a3543db0e6277bcb3835fc0ecac06edbe 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests calling NoImplicitConversion using a ExtendsNoImplicitConversion parameter,
    being that the latter defines a new conversion operator for the former, and this one
index abbef623168f8cb45887937627c87f88ab0d92da..bb2511daebea65c3b5664e55831412089b4be764 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for libsample's Point multiply operator defined in libother module.'''
 
index bde2f52363d04e085355dbc907ea096f841b843e..2fd66a51f4f0fe20d651b42c64b6347024076ac9 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 from importlib import reload
 import os
index d6c3564363f66ed0004f9cc399a6ae73976abb72..3c63929f1a371a0972aafbd7ce4f62695bf6f847 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests calling Str constructor using a Number parameter, being that number defines
    a cast operator to Str.'''
index d2cd7de5ba63d35e7a105f236410d566a7ae727e..6764cc0f7607c1dd5b72146d664a2c36ffa181ab 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 459f474f11b9b358c7a7e4676e1091860d713400..13c59f91f55d59df5093c198f283e89e1ae40d8b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for OtherDerived class'''
 
index 198c71693756f56665f1f52be0924c5c442a22f4..236dda6b07b7b735c1079a46ee30461bd2c150df 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for a class that holds a void pointer.'''
 
index 8db3e566b13aed6a00c006705c5fdb5024e1a4b6..a22f65316ebbcf9c5aa67d600349f601f548420a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for functions signature'''
 
index fd5c7fa09bc466350d78a5f8da6a4395a87c9dc2..1c9466e242575a23005d1633fab086209a26301d 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for the SmartPtrTester class'''
 
index 4b5f1d2702068930e93bb196957e3c06deffd2d4..9c4b42fa869f41cd78acf18193ceef684c3da4eb 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2024 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """PYSIDE-2404: Test whether star imports work as they require special handling
    by the lazy initialization."""
index 36ab43ae3fc7869ef76b1e0ba4b3447326d8c51c..b4e5515fa53b62b0006ee90bdc06e0bc39399585 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 791d3bdce52d48a680af2939af8d115e56dfc3a5..67f45e7babfddf9ed4d4d5182cb85a9eda1474ae 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for type discovery'''
 
@@ -13,7 +14,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from shiboken_paths import init_paths
 init_paths()
 
-from sample import Abstract, Base1, Derived
+from sample import (Abstract, Base1, Derived,
+                    MDerived1, SonOfMDerived1, MDerived3)
 from other import OtherMultipleDerived
 
 
@@ -32,14 +34,18 @@ class TypeDiscoveryTest(unittest.TestCase):
     def testMultipleInheritance(self):
         obj = OtherMultipleDerived.createObject("Base1")
         self.assertEqual(type(obj), Base1)
-        # PYSIDE-868: In case of multiple inheritance, a factory
-        # function will return the base class wrapper.
+        # PYSIDE-868: In case of single line direct inheritance,
+        # a factory function will return the class wrapper
+        # of the derived class.
         obj = OtherMultipleDerived.createObject("MDerived1")
-        self.assertEqual(type(obj), Base1)
+        self.assertEqual(type(obj), MDerived1)
         obj = OtherMultipleDerived.createObject("SonOfMDerived1")
-        self.assertEqual(type(obj), Base1)
+        self.assertEqual(type(obj), SonOfMDerived1)
         obj = OtherMultipleDerived.createObject("MDerived3")
-        self.assertEqual(type(obj), Base1)
+        self.assertEqual(type(obj), MDerived3)
+        # PYSIDE-868: OtherMultipleDerived inherits
+        # OtherBase, Base1. In this case, a factory
+        # function will return the base class wrapper.
         obj = OtherMultipleDerived.createObject("OtherMultipleDerived")
         self.assertEqual(type(obj), Base1)
 
index 15a988326037e5fec6d012bf37f13fa0ad2c35af..e99256f35fcfb5a7a12a50d838424c23b50a64b9 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests user defined primitive type from a required module.'''
 
index b9251b428ba8bb32080c20a0cba56f801abe7c89..a5fe04ecd0f65ffdbe1f41e38ac698175f75842d 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 27aaee7d14bc02d5b4b1d976e5d7a70204a4e4b1..5b0624376c6b8c78769f7bb2f0ed544d8f944f69 100644 (file)
@@ -40,6 +40,7 @@ public:
                                     const QString &) const override;
     const QLoggingCategory &loggingCategory() const override;
     QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &link) const override;
+    Image resolveImage(const QString &href, const QString &) const;
 };
 
 // QtXmlToSphinxDocGeneratorInterface
@@ -63,11 +64,18 @@ const QLoggingCategory &QtXmlToSphinxDocGenerator::loggingCategory() const
     return lcQtXmlToSphinx();
 }
 
-QtXmlToSphinxLink QtXmlToSphinxDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const
+QtXmlToSphinxLink
+    QtXmlToSphinxDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const
 {
     return link;
 }
 
+QtXmlToSphinxDocGeneratorInterface::Image
+    QtXmlToSphinxDocGenerator::resolveImage(const QString &href, const QString &) const
+{
+    return {href, href};
+}
+
 static bool run(const QString &fileName)
 {
     QtXmlToSphinxDocGenerator generator;
index 45cecd1a190378ae0f4f6e945921f78a609b0a4a..a06f752bbeca5b70089c51616b1f3c06470bea93 100644 (file)
@@ -41,7 +41,7 @@ QtXmlToSphinxLink QtXmlToSphinxTest::resolveLink(const QtXmlToSphinxLink &link)
 
 QString QtXmlToSphinxTest::transformXml(const QString &xml) const
 {
-    return QtXmlToSphinx(this, m_parameters, xml).result();
+    return QtXmlToSphinx(this, m_parameters, xml, QString{} /* context */).result();
 }
 
 void QtXmlToSphinxTest::testTable_data()
index fc812feb82496548f75402602b30c3b2ffc807a6..f693008b08d77c50f7cf1cd6698c799f9b93dd6a 100644 (file)
@@ -149,6 +149,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sample-binding.txt.in"
                "${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt" @ONLY)
 
 shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+    SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
 
 add_custom_command(
     OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
@@ -157,6 +160,7 @@ add_custom_command(
         ${tool_wrapper}
         $<TARGET_FILE:Shiboken6::shiboken6>
         --project-file=${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt
+        ${UNOPTIMIZE}
         ${GENERATOR_EXTRA_FLAGS}
     DEPENDS ${sample_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
index 456886614b4a291b158eeeede6e22a8803425e69..688e354458beae38f5ecec29caa4ecef4b0cced3 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 89e87be1d794d23bd102a4e99c1352f2ffbb292c..f9ccd0b121f91d4147229649183ba68b604ce3d3 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Abstract class'''
 
@@ -79,7 +80,7 @@ class AbstractTest(unittest.TestCase):
     def testEnumParameterOnVirtualMethodCall(self):
         '''testEnumParameterOnVirtualMethodCall'''
         c = Concrete()
-        c.callVirtualGettingEnum(Abstract.Short)
+        c.callVirtualGettingEnum(Abstract.PrintFormat.Short)
         self.assertTrue(c.virtual_getting_enum)
 
 
index 0b5680143dbfb4ff7ce88f9b560f09405f83a05f..cd7132ad93ac383974d95869b7375781194049ff 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for added functions.'''
 
index 2a739033bff3189c7c69de0a4584db66e1e05f82..01b77531e4e977932dc0f4ae03e56216aa1a5c43 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for added functions with nested and multi-argument container types.'''
 
index b0ca56a6d1d9a8e3011f57c6f83409f65e7ec264..5765544043c5b59e595b00001857869bc2576bd3 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for method arguments modifications performed as described on typesystem.'''
 
index 0d73bca1c97b30d34d9492c50727775ac7f27ab7..a8baeb098b68875c5955eed69a607fc7e698ffcf 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for NumPy Array types.'''
 
index ad65d58db625337d1ebf1837857ab79aaeec8c24..379d0b69ef5c53bcb2ca6d7a71d6a411c71e3e85 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for Array types (PySequence).'''
 
index a7e7a7210a59bb1460b136e541f0108055b35007..0292a3cdb262e8861fdfdf6fa0e00f76f7eba4b0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Unit test for bug#554'''
 
index c470fe7239b08c2af4eefcf2eb791a2bfe960a33..76fa846536c9f88a16ccc11ea552674417b2b047 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -14,7 +15,7 @@ init_paths()
 from sample import ObjectType
 
 
-class NewStyle(object):
+class NewStyle:
     def name(self):
         return "NewStyle"
 
index e51a899faeb31a1f55efea53b91abde56d065810..a3771ef94fbf8da7183923ea5595a6ec97da7112 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f0ac706264cd417528f33f8ddf115522d3b563ce..dc39aaaabecb6e85a28d5454cbf00b1802490db3 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''The BlackBox class has cases of ownership transference between C++ and Python.'''
 
index 1eeb3d44679ed5b9f7ff01fc8119db48a8820c72..63b8b8fa32a763163ee766665301fb008fb1457f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Simple test case for accessing the exposed C++ class fields.'''
 
index 4caebc62a2525ea5d56a53cccdb2244b04e6e4a9..dfde29ae71b1c754e0d47db4d98741b88d22af95 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Collector class' shift operators.'''
 
index 454aff100b4c1bfd526bcaff1d4c11ccc809bf5b..83e5c26d0bbebfb6fc0353d0d2602548e145104b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Complex class'''
 
index 7e76245b103482e42f8e6cec5f7cc52a5a30c379..aa30637e73b0f6d7b95108f30f389cf2d2e156f6 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for implicit conversion generated by conversion operator.'''
 
index db539d1b902e63a4e2bf8c9faaa3f06ec90304b6..95437676d623432476ad47b25e82606d2ad6649a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for deep copy of objects'''
 
index 5e2695d7218249a88eeea0ef238cc081acda974f..9381164cb2e3b5dd6ed4541e4df94186ae0edb4d 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for proper generation of constructor altered by conversion-rule tag.'''
 
index 4e4ae26032e49168eecae265be9a92b8fea7b725..95cc33375b6417a6d7515d6f62836bdf537f6df6 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
index 2b6efcf1865cecb7961f74788c30567158217c6c..48a9414c5593bad611ba82d1a723d22d9521dc2d 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for python conversions types '''
 
index 0d39c5f96861ba2d9876b6823de370c08f859c26..9aeeeed20ecbd7cf18ded37f02c0dc5ec23cf6bf 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for the method overload decisor.'''
 
@@ -39,13 +40,13 @@ class DecisorTest(unittest.TestCase):
         objecttype = ObjectType()
         objectmodel = ObjectModel()
         self.assertEqual(ObjectModel.receivesObjectTypeFamily(objecttype),
-                         ObjectModel.ObjectTypeCalled)
+                         ObjectModel.MethodCalled.ObjectTypeCalled)
         self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objecttype),
-                            ObjectModel.ObjectModelCalled)
+                            ObjectModel.MethodCalled.ObjectModelCalled)
         self.assertEqual(ObjectModel.receivesObjectTypeFamily(objectmodel),
-                         ObjectModel.ObjectModelCalled)
+                         ObjectModel.MethodCalled.ObjectModelCalled)
         self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objectmodel),
-                            ObjectModel.ObjectTypeCalled)
+                            ObjectModel.MethodCalled.ObjectTypeCalled)
 
 
 if __name__ == '__main__':
index 57a792ae274100b9eb6dc207db3b290bb289d731..09ad6f947df2b525434eccace551db27778dc5b8 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index c371df94f03553d65db0adf41d3ff720e2f89d52..eafbd7c543a226e042f1a477c9f161d50fb7ac64 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 346f29136c30f7783271d3dd88ecda50fb8bcc5d..b251ce8c7bccda8dd8c3b3e0712804a295326e03 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Derived class'''
 
@@ -57,11 +58,11 @@ class DerivedTest(unittest.TestCase):
 
         result = derived.otherOverloaded(1, 2, True, 3.3)
         self.assertEqual(type(result), Derived.OtherOverloadedFuncEnum)
-        self.assertEqual(result, sample.Derived.OtherOverloadedFunc_iibd)
+        self.assertEqual(result, sample.Derived.OtherOverloadedFuncEnum.OtherOverloadedFunc_iibd)
 
         result = derived.otherOverloaded(1, 2.2)
         self.assertEqual(type(result), Derived.OtherOverloadedFuncEnum)
-        self.assertEqual(result, Derived.OtherOverloadedFunc_id)
+        self.assertEqual(result, Derived.OtherOverloadedFuncEnum.OtherOverloadedFunc_id)
 
     def testOverloadedMethodCallWithDifferentNumericTypes(self):
         '''Test if the correct overloaded method accepts a different numeric type as argument.'''
index aa21a0f7ee677c84ec052ccad2c83e4ea5deb915..04fb0a7e0be523485a62e974d7fc1da792a3d589 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for virtual methods.'''
 
index f1859260ea3df9403123de33ae7f2c4c8ed73a45..b42abcf3fe4573036b9fe47ce3b6cad2d6e1be2f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for <add-function> with const char* as argument'''
 
index 276b8d8949483d97ccac4ff3d6f360cd39d9b346..d4369b420689cdc7eb583ecd9af714b43fd81e5e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Python representation of C++ enums.'''
 
@@ -28,8 +29,9 @@ class EnumTest(unittest.TestCase):
     '''Test case for Python representation of C++ enums.'''
 
     def testHashability(self):
-        self.assertEqual(hash(SampleNamespace.TwoIn), hash(SampleNamespace.TwoOut))
-        self.assertNotEqual(hash(SampleNamespace.TwoIn), hash(SampleNamespace.OneIn))
+        self.assertEqual(hash(SampleNamespace.InValue.TwoIn), hash(SampleNamespace.OutValue.TwoOut))
+        self.assertNotEqual(hash(SampleNamespace.InValue.TwoIn),
+                            hash(SampleNamespace.InValue.OneIn))
 
     def testEnumValuesInsideEnum(self):
         '''Enum values should be accessible inside the enum as well as outside.'''
@@ -49,13 +51,13 @@ class EnumTest(unittest.TestCase):
     def testBuildingEnumWithDefaultValue(self):
         '''Enum constructor with default value'''
         enum = SampleNamespace.Option()
-        self.assertEqual(enum, SampleNamespace.None_)
+        self.assertEqual(enum, SampleNamespace.Option.None_)
 
     def testEnumConversionToAndFromPython(self):
         '''Conversion of enum objects from Python to C++ back again.'''
-        enumout = SampleNamespace.enumInEnumOut(SampleNamespace.TwoIn)
-        self.assertTrue(enumout, SampleNamespace.TwoOut)
-        self.assertEqual(repr(enumout), repr(SampleNamespace.TwoOut))
+        enumout = SampleNamespace.enumInEnumOut(SampleNamespace.InValue.TwoIn)
+        self.assertTrue(enumout, SampleNamespace.OutValue.TwoOut)
+        self.assertEqual(repr(enumout), repr(SampleNamespace.OutValue.TwoOut))
 
     def testEnumConstructorWithTooManyParameters(self):
         '''Calling the constructor of non-extensible enum with the wrong number of parameters.'''
@@ -68,9 +70,9 @@ class EnumTest(unittest.TestCase):
     def testEnumItemAsDefaultValueToIntArgument(self):
         '''Calls function with an enum item as default value to an int argument.'''
         self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(),
-                         SampleNamespace.ZeroIn)
-        self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(SampleNamespace.ZeroOut),  # noqa E:501
-                         SampleNamespace.ZeroOut)
+                         SampleNamespace.InValue.ZeroIn)
+        self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(SampleNamespace.OutValue.ZeroOut),  # noqa E:501
+                         SampleNamespace.OutValue.ZeroOut)
         self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(123), 123)
 
     def testAnonymousGlobalEnums(self):
@@ -89,20 +91,21 @@ class EnumTest(unittest.TestCase):
         self.assertEqual(sum, 1)
 
     def testSetEnum(self):
-        event = Event(Event.ANY_EVENT)
-        self.assertEqual(event.eventType(), Event.ANY_EVENT)
-        event.setEventType(Event.BASIC_EVENT)
-        self.assertEqual(event.eventType(), Event.BASIC_EVENT)
-        event.setEventTypeByConstRef(Event.SOME_EVENT)
-        self.assertEqual(event.eventType(), Event.SOME_EVENT)
-        event.setEventTypeByConstPtr(Event.BASIC_EVENT)
-        self.assertEqual(event.eventType(), Event.BASIC_EVENT)
+        event = Event(Event.EventType.ANY_EVENT)
+        self.assertEqual(event.eventType(), Event.EventType.ANY_EVENT)
+        event.setEventType(Event.EventType.BASIC_EVENT)
+        self.assertEqual(event.eventType(), Event.EventType.BASIC_EVENT)
+        event.setEventTypeByConstRef(Event.EventType.SOME_EVENT)
+        self.assertEqual(event.eventType(), Event.EventType.SOME_EVENT)
+        event.setEventTypeByConstPtr(Event.EventType.BASIC_EVENT)
+        self.assertEqual(event.eventType(), Event.EventType.BASIC_EVENT)
 
     def testEnumArgumentWithDefaultValue(self):
         '''Option enumArgumentWithDefaultValue(Option opt = UnixTime);'''
-        self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(), SampleNamespace.UnixTime)
-        self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(SampleNamespace.RandomNumber),  # noqa E:501
-                         SampleNamespace.RandomNumber)
+        self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(),
+                         SampleNamespace.Option.UnixTime)
+        self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(SampleNamespace.Option.RandomNumber),  # noqa E:501
+                         SampleNamespace.Option.RandomNumber)
 
 
 class MyEvent(Event):
@@ -123,7 +126,7 @@ class EnumOverloadTest(unittest.TestCase):
         '''Overload with Enums and ints with default value'''
         o = ObjectType()
 
-        self.assertEqual(o.callWithEnum('', Event.ANY_EVENT, 9), 81)
+        self.assertEqual(o.callWithEnum('', Event.EventType.ANY_EVENT, 9), 81)
         self.assertEqual(o.callWithEnum('', 9), 9)
 
 
@@ -131,7 +134,8 @@ class EnumOperators(unittest.TestCase):
     '''Test case for operations on enums'''
 
     def testInequalitySameObject(self):
-        self.assertFalse(Event.ANY_EVENT != Event.ANY_EVENT)
+        self.assertFalse(Event.EventType.ANY_EVENT
+                         != Event.EventType.ANY_EVENT)
 
 
 if __name__ == '__main__':
index 42ae23961da279b3a9f8cf09f956e88b162c3520..5696f968aa106021161347cbec3b59869c427bd0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -40,7 +41,7 @@ class TestEnumFromRemovedNamespace(unittest.TestCase):
     def testEnumPromotedToUpperNamespace(self):
         sample.UnremovedNamespace
         sample.UnremovedNamespace.RemovedNamespace3_Enum
-        sample.UnremovedNamespace.RemovedNamespace3_Enum_Value0
+        sample.UnremovedNamespace.RemovedNamespace3_Enum.RemovedNamespace3_Enum_Value0
         sample.UnremovedNamespace.RemovedNamespace3_AnonymousEnum_Value0
 
     def testNestedFunctionFromRemovedNamespace(self):
index 8e13d5d46f05e644a6384354961bf75927a5ee22..f9286dd224580f5cca13c8c52cec37721aa6cf9a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Simple event loop dispatcher test.'''
 
@@ -39,7 +40,7 @@ class TestEventLoop(unittest.TestCase):
         objs = [ObjectType(), NoOverride(), Override()]
 
         evaluated = ObjectType.processEvent(objs,
-                                            Event(Event.BASIC_EVENT))
+                                            Event(Event.EventType.BASIC_EVENT))
 
         self.assertEqual(evaluated, 3)
         self.assertTrue(objs[2].called)
index 8b854fca6baf0a04f6725b5d3ee6ea314a3dd4b2..1276eb05078754b0e819b35f2c5e370bd51b55a1 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 from random import random
@@ -64,7 +65,7 @@ class TestEventLoopWithThread(unittest.TestCase):
         thread.start()
 
         evaluated = ObjectType.processEvent(objs,
-                                            Event(Event.BASIC_EVENT))
+                                            Event(Event.EventType.BASIC_EVENT))
 
         thread.join()
 
index d9e6b377f1ac37f4c8bfe4cd80b3143971b5853a..95555b330a5807cc69f101fb316c7ac23883e1c7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index df805093f54e3988ee292d47e5fe42db3c8e013b..e5c1e9f9fec1bb9556f823cd49e92322ba24b966 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,7 +18,7 @@ class TestFilters(unittest.TestCase):
 
     def testAnd(self):
 
-        f1 = Data(Data.Name, "joe")
+        f1 = Data(Data.Field.Name, "joe")
         f2 = Union()
 
         inter = f1 & f2
index af22328c54e487f16b14ea966307712915123ddd..cddea4035a8bf556aee65365f8c2c98ce0349524 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 ''' Test case for a class that holds a unknown handle object.
     Test case for BUG #1105.
index c41f5cc066438eafac611141aebb3d85491a2698..4643f6103c881706e5b42da8b1e3428bce429abd 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for __hash__'''
 
index feb78d04566872a77e2db35ca5e3772550c0d592..1ee44ac59aab0092f9442f38303913fbebd7dc11 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 0816662814cb5fe0c551e02976fc31603ce3ea4f..b114ad0c1393998726c310bc17dba22d90c57fa8 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for inplicit converting C++ numeric types.'''
 
index ebafe0c5273ace81db9b64857f7e636bd7408e06..f6622fb34f23070b178f2a10bcbb438a529dddab 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for implicit conversions'''
 
@@ -22,24 +23,24 @@ class ImplicitConvTest(unittest.TestCase):
     def testImplicitConversions(self):
         '''Test if overloaded function call decisor takes implicit conversions into account.'''
         ic = ImplicitConv.implicitConvCommon(ImplicitConv())
-        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorNone)
+        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorNone)
 
         ic = ImplicitConv.implicitConvCommon(3)
-        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorOne)
+        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorOne)
         self.assertEqual(ic.objId(), 3)
 
-        ic = ImplicitConv.implicitConvCommon(ImplicitConv.CtorThree)
-        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorThree)
+        ic = ImplicitConv.implicitConvCommon(ImplicitConv.CtorEnum.CtorThree)
+        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorThree)
 
         obj = ObjectType()
         ic = ImplicitConv.implicitConvCommon(obj)
-        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorObjectTypeReference)
+        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorObjectTypeReference)
 
         ic = ImplicitConv.implicitConvCommon(42.42)
         self.assertEqual(ic.value(), 42.42)
 
         ic = ImplicitConv(None)
-        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorPrimitiveType)
+        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorPrimitiveType)
 
 
 if __name__ == '__main__':
index 28d62486adcd85349ea66746d49e1ed752e3b423..8305cd40bd9a50d0b782d3a37ce0de52f812ffa4 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for finding scope in cases involving inheritance.'''
 
index f673a7807d24e22f839ff1d73fd76eba0d67de6e..93399510041de4ea2445e66a505fab52f8481731 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for std::list container conversions'''
 
index 721f33483dc6117a41ca177de4e1a88490ac4e77..39e827a648984fd74140ee3c3d645711d8b6e941 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index defa9ca713953db40506a2fc880d8eba2ec3c1f7..f062b53c704a28375ef72cd4e10ec3d55c1a92ae 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -35,7 +36,7 @@ class IntListTest(unittest.TestCase):
         '''IntList constructor receives no parameter.'''
         il = IntList()
         self.assertEqual(len(il), 0)
-        self.assertEqual(il.constructorUsed(), IntList.NoParamsCtor)
+        self.assertEqual(il.constructorUsed(), IntList.CtorEnum.NoParamsCtor)
 
     def testIntListCtor_int(self):
         '''IntList constructor receives an integer.'''
@@ -43,7 +44,7 @@ class IntListTest(unittest.TestCase):
         il = IntList(value)
         self.assertEqual(len(il), 1)
         self.assertEqual(il[0], value)
-        self.assertEqual(il.constructorUsed(), IntList.IntCtor)
+        self.assertEqual(il.constructorUsed(), IntList.CtorEnum.IntCtor)
 
     def testIntListCtor_IntList(self):
         '''IntList constructor receives an IntList object.'''
@@ -52,7 +53,7 @@ class IntListTest(unittest.TestCase):
         self.assertEqual(len(il1), len(il2))
         for i in range(len(il1)):
             self.assertEqual(il1[i], il2[i])
-        self.assertEqual(il2.constructorUsed(), IntList.CopyCtor)
+        self.assertEqual(il2.constructorUsed(), IntList.CtorEnum.CopyCtor)
 
     def testIntListCtor_ListOfInts(self):
         '''IntList constructor receives an integer list.'''
@@ -61,7 +62,7 @@ class IntListTest(unittest.TestCase):
         self.assertEqual(len(il), len(ints))
         for i in range(len(il)):
             self.assertEqual(il[i], ints[i])
-        self.assertEqual(il.constructorUsed(), IntList.ListOfIntCtor)
+        self.assertEqual(il.constructorUsed(), IntList.CtorEnum.ListOfIntCtor)
 
     def testIntListAttributeTypeCheck(self):
         '''Attribute values to IntList.'''
index d883adf4708cc8d9ac6bb766150a24e0b37fd8ed..04b2ff351e5398dbf82c3fc29d8fb85dbbc569a7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index bb35b2bb1f99a0a0aefa5d2cd6bf03b2f1144592..d0bcd415d8f52f69602ba3fa44e1906b5e55d707 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for returning invalid types in a virtual function'''
 
index 10591fec6c1035526de341acba080f1751b7a253..1c431763e82f32b19bf8e4ffd46cb972433500f0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b668bfd90c896172372193a0d86d3b82fa24fa15..4d113722f1a2f4b5908f9b40b50cdcdcdc55a323 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for std::list container conversions'''
 
index acd47634a1dc408221f2a26279feafefcd7179b7..5ea6bf8d824a7a06f3d7327405acf83cb4bad560 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Simple test with a blocking C++ method that should allow python
    threads to run.'''
index fa99ad2e7308e4d4747d6ce6a2171300baa201df..fd319617a98a7f533d8f603b0868ba86128cd00d 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for std::map container conversions'''
 
index 4d7eeda963985b9c4a18f35d087065f3d3b2a78f..cbde1b5798e1afc87f2ef62f978cb57be9dc11cb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -17,7 +18,7 @@ class MetaA(type):
     pass
 
 
-class A(object):
+class A:
     __metaclass__ = MetaA
 
 
index 8d324db590018407f1c16cba2e582b8236971071..02e3b1ba71094e6f7174337b592717e9e58b9ef3 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for virtual methods in multiple inheritance scenarios'''
 
@@ -46,7 +47,7 @@ class CppVirtualTest(unittest.TestCase):
     def testCpp(self):
         '''C++ calling C++ virtual method in multiple inheritance scenario'''
         obj = ImplementsNone()
-        self.assertTrue(ObjectType.processEvent([obj], Event(Event.BASIC_EVENT)))
+        self.assertTrue(ObjectType.processEvent([obj], Event(Event.EventType.BASIC_EVENT)))
         self.assertRaises(AttributeError, getattr, obj, 'event_processed')
 
         self.assertEqual(obj.callSum0(1, 2, 3), 6)
@@ -58,7 +59,7 @@ class PyVirtualTest(unittest.TestCase):
     def testEvent(self):
         '''C++ calling Python reimplementation of virtual in multiple inheritance'''
         obj = ImplementsBoth()
-        self.assertTrue(ObjectType.processEvent([obj], Event(Event.BASIC_EVENT)))
+        self.assertTrue(ObjectType.processEvent([obj], Event(Event.EventType.BASIC_EVENT)))
         self.assertTrue(obj.event_processed)
 
         self.assertEqual(obj.callSum1(1, 2, 3), 12)
index fa848160052444e85bc5df3808541cf8d776335d..50e4970ced94459927445829b68e2d33fae9c43c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for multiple inheritance in mixed Python/C++ scenarios'''
 
@@ -16,7 +17,7 @@ init_paths()
 from sample import ObjectType
 
 
-class Base(object):
+class Base:
     '''Base Python class'''
 
     def __init__(self):
index e23503effde2fd10f2445eb4e51c92a74ca1a085..786e4a727d578ea90b7e4c043cf89ef9fc666f73 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index b5663a04e95ee36d74be202add154b8bfc9e21fc..090c4f98b0045aa8c5f4e29ae38b4948c28f92ef 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for objects that keep references to other object without owning them
    (e.g. model/view relationships).'''
index dced14396cb69f1abbad3e115bf0de9f94ef1ec3..3d9ffa2325f9731a361d65fb031aceaec36dcc5e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for method modifications performed as described on type system. '''
 
@@ -115,16 +116,19 @@ class ModificationsTest(unittest.TestCase):
     def testOverloadedMethodModifications(self):
         '''Tests modifications to an overloaded method'''
         # overloaded(int, bool[removed], int, double)
-        self.assertEqual(self.mods.overloaded(1, 2, 3.1), Modifications.Overloaded_ibid)
+        self.assertEqual(self.mods.overloaded(1, 2, 3.1),
+                         Modifications.OverloadedModFunc.Overloaded_ibid)
         # overloaded(int, bool, int[removed,default=321], int)
-        self.assertEqual(self.mods.overloaded(1, True, 2), Modifications.Overloaded_ibii)
+        self.assertEqual(self.mods.overloaded(1, True, 2),
+                         Modifications.OverloadedModFunc.Overloaded_ibii)
         # the others weren't modified
-        self.assertEqual(self.mods.overloaded(1, True, 2, False), Modifications.Overloaded_ibib)
+        self.assertEqual(self.mods.overloaded(1, True, 2, False),
+                         Modifications.OverloadedModFunc.Overloaded_ibib)
         self.assertEqual(self.mods.overloaded(1, False, 2, Point(3, 4)),
-                         Modifications.Overloaded_ibiP)
+                         Modifications.OverloadedModFunc.Overloaded_ibiP)
         self.assertRaises(TypeError, self.mods.overloaded, 1, True, Point(2, 3), Point(4, 5))
         self.assertEqual(self.mods.over(1, True, Point(2, 3), Point(4, 5)),
-                         Modifications.Overloaded_ibPP)
+                         Modifications.OverloadedModFunc.Overloaded_ibPP)
 
     def testPointArrayModification(self):
         points = (Point(1, 1), Point(2, 2))
@@ -209,7 +213,7 @@ class ModificationsTest(unittest.TestCase):
         # qualified by the enum scope.
         modifications = Modifications()
         modifications.setEnumValue()
-        self.assertEqual(modifications.enumValue(), Modifications.TestEnumValue2)
+        self.assertEqual(modifications.enumValue(), Modifications.TestEnum.TestEnumValue2)
 
     def testSetGetAttro(self):
         modifications = Modifications()
index 9791a34919cfafad46662f0105c5ab5afeb2a505..8b99a557c18aa928c4b9d22a6e4373e6dfbd5f70 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests cases for ConstructorWithModifiedArgument class.'''
 
index dcb487f1a664cdd15d5679f898e44678f9973a04..244820b6f8bd88d615bca4cc60f8e2e1d41e5dac 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for modified virtual methods.'''
 
index fc6b26c3f09dd9fdb2f682a17368ac67b88ae7ef..53cda8a800bcef153b857a86dc4b07e03ff9e07e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for multiple inheritance'''
 
index 7497714a821c0d449cf108e66fbbfdca31812b61..4b33cc63eb4e653731462461564d0b693f892a7f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for multiple inheritance'''
 
index 64a6792ac5473cc1f893005d7398237e13b64170..0d67c7497c1fbd3055ef37b8f072f55c967c6686 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for std::map container conversions'''
 
@@ -60,8 +61,8 @@ class TestClassesUnderNamespace(unittest.TestCase):
 
     def testInlineNamespaces(self):
         cls = SampleNamespace.ClassWithinInlineNamespace()
-        cls.setValue(SampleNamespace.EWIN_Value1)
-        self.assertEqual(cls.value(), SampleNamespace.EWIN_Value1)
+        cls.setValue(SampleNamespace.EnumWithinInlineNamespace.EWIN_Value1)
+        self.assertEqual(cls.value(), SampleNamespace.EnumWithinInlineNamespace.EWIN_Value1)
 
 
 if __name__ == '__main__':
index 0e7dfbee19a6f3d44191a7750f4a475cfb7a4368..3a5510e3aa5c074dc2e4a6017c8e2d8677d4af6b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index bc8d29e500ee938163f9bb180c98cf360175f3e0..3a2340bc7ed1d6b6052a68b2265edc188dee4cb2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for ...'''
 
index a10547728e229f36993ccaa5ba316999a0630130..257bd5ef7c119e7ec622835331a3320906ee2ab2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 hasNumPy = False
 
index 7be239fc47d6d8ac88508526095a3b2debd19bbd..8dded99df8b0fdc785e920c3fa2606dfec1b8e6e 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index f714a4fc8ce7ff97ecf41768a957dc0e67e9197b..50569b88c225b220012e696a9049d337c2c61760 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 42094a463ae741b98ecb140aa2014eaffcf52b20..2f02859a9505731281fa9ec8c506cb2b9d65c8b7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import sys
 
index ead68ba1385a52de231ba2670db32402c3018519..73866e3a2ce6ce78d60ebeba565a70d59260ee63 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests ObjectType class of object-type with privates copy constructor and = operator.'''
 
index 285e2313bcb78df8efcd9f8392ea3cf1702acd7a..add9f947faf2919e571a3e00b0e0010aa389db78 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 8f74af3abac2f3ddbc19474fc4240311c369010e..ab8f18ba642c647f58c4e3f062b47cc1c4b3cd8c 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 677b892816cb0479d2a1cbc242f7367c4bae6f63..9f6012164b478742ea55b9d029a3c349b9f7dd69 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests cases for ObjectTypeLayout class.'''
 
index ceeee6c8d89ce1f1f777a771371e72f1d3cb7316..3123c7b612f9965074e6484b50aa276481c48245 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 5fa6f824e59a4281462e18639c2f6c2a9c572ba7..370c5c8f71072cd8f44bf637afa580d7eb1de6f4 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 87a8cdb1f8b231241bb351726259f8cd5fda6605..2d48556ded138a3a1bc3ba2ef48283669c431fb2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for OddBool user's primitive type conversion.'''
 
@@ -62,7 +63,7 @@ class OddBoolTest(unittest.TestCase):
         self.assertEqual(t1, t2)
 
     def testSpaceshipOperator(self):
-        if not SpaceshipComparisonTester.HasSpaceshipOperator:
+        if not SpaceshipComparisonTester.Enabled.HasSpaceshipOperator:
             print("Skipping Spaceship Operator test")
             return
         t1 = SpaceshipComparisonTester(42)
index bcb154c52061be314d0ef9c426aa2865075f6efa..5a26b5096dfab85841dfed75cdd0b543064444d7 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 84442306a249477e005fd306fccb1258549f1624..6dbce0f94b35d1690af0754243ec97a9bf45431b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for overflowing C++ numeric types.'''
 
index 060d915103cf23aacce65965b1b07e2d30f2374a..1b63aa33ee674db3165a8e32823fb54af8f2ffde 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for overload sorting'''
 
@@ -17,7 +18,7 @@ from sample import (CustomOverloadSequence, ImplicitBase, ImplicitConv,
                     ImplicitTarget, SortedOverload)
 
 
-class Dummy(object):
+class Dummy:
     pass
 
 
@@ -69,8 +70,8 @@ class DeepOverloadSorting(unittest.TestCase):
 
 class EnumOverIntSorting(unittest.TestCase):
     def testEnumOverInt(self):
-        ic = ImplicitConv(ImplicitConv.CtorTwo)
-        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorTwo)
+        ic = ImplicitConv(ImplicitConv.CtorEnum.CtorTwo)
+        self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorTwo)
 
 
 class TestCustomOverloadSequence(unittest.TestCase):
index 62fa8d8d24f0f9ffedfc22719b1d3fce8f119834..e68bd0aad4a93d25da86d03e8167c5408e792712 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Overload class'''
 
@@ -34,32 +35,33 @@ class OverloadTest(unittest.TestCase):
     def testOverloadMethod0(self):
         '''Check overloaded method call for signature "overloaded()".'''
         overload = Overload()
-        self.assertEqual(overload.overloaded(), Overload.Function0)
+        self.assertEqual(overload.overloaded(), Overload.FunctionEnum.Function0)
 
     def testOverloadMethod1(self):
         '''Check overloaded method call for signature "overloaded(Size*)".'''
         overload = Overload()
         size = Size()
-        self.assertEqual(overload.overloaded(size), Overload.Function1)
+        self.assertEqual(overload.overloaded(size), Overload.FunctionEnum.Function1)
 
     def testOverloadMethod2(self):
         '''Check overloaded method call for signature "overloaded(Point*, ParamEnum)".'''
         overload = Overload()
         point = Point()
-        self.assertEqual(overload.overloaded(point, Overload.Param1), Overload.Function2)
+        self.assertEqual(overload.overloaded(point, Overload.ParamEnum.Param1),
+                         Overload.FunctionEnum.Function2)
 
     def testOverloadMethod3(self):
         '''Check overloaded method call for signature "overloaded(const Point&)".'''
         overload = Overload()
         point = Point()
-        self.assertEqual(overload.overloaded(point), Overload.Function3)
+        self.assertEqual(overload.overloaded(point), Overload.FunctionEnum.Function3)
 
     def testDifferentReturnTypes(self):
         '''Check method calls for overloads with different return types.'''
         overload = Overload()
         self.assertEqual(overload.differentReturnTypes(), None)
-        self.assertEqual(overload.differentReturnTypes(Overload.Param1), None)
-        self.assertEqual(overload.differentReturnTypes(Overload.Param0, 13), 13)
+        self.assertEqual(overload.differentReturnTypes(Overload.ParamEnum.Param1), None)
+        self.assertEqual(overload.differentReturnTypes(Overload.ParamEnum.Param0, 13), 13)
 
     def testIntOverloads(self):
         overload = Overload()
@@ -69,65 +71,67 @@ class OverloadTest(unittest.TestCase):
 
     def testIntDoubleOverloads(self):
         overload = Overload()
-        self.assertEqual(overload.intDoubleOverloads(1, 2), Overload.Function0)
-        self.assertEqual(overload.intDoubleOverloads(1, 2.0), Overload.Function0)
-        self.assertEqual(overload.intDoubleOverloads(1.0, 2), Overload.Function1)
-        self.assertEqual(overload.intDoubleOverloads(1.0, 2.0), Overload.Function1)
+        self.assertEqual(overload.intDoubleOverloads(1, 2), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.intDoubleOverloads(1, 2.0), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.intDoubleOverloads(1.0, 2), Overload.FunctionEnum.Function1)
+        self.assertEqual(overload.intDoubleOverloads(1.0, 2.0), Overload.FunctionEnum.Function1)
 
     def testWrapperIntIntOverloads(self):
         overload = Overload()
-        self.assertEqual(overload.wrapperIntIntOverloads(Point(), 1, 2), Overload.Function0)
-        self.assertEqual(overload.wrapperIntIntOverloads(Polygon(), 1, 2), Overload.Function1)
+        self.assertEqual(overload.wrapperIntIntOverloads(Point(), 1, 2),
+                         Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.wrapperIntIntOverloads(Polygon(), 1, 2),
+                         Overload.FunctionEnum.Function1)
 
     def testDrawTextPointAndStr(self):
         overload = Overload()
-        self.assertEqual(overload.drawText(Point(), Str()), Overload.Function0)
-        self.assertEqual(overload.drawText(Point(), ''), Overload.Function0)
-        self.assertEqual(overload.drawText(PointF(), Str()), Overload.Function1)
-        self.assertEqual(overload.drawText(PointF(), ''), Overload.Function1)
+        self.assertEqual(overload.drawText(Point(), Str()), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.drawText(Point(), ''), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.drawText(PointF(), Str()), Overload.FunctionEnum.Function1)
+        self.assertEqual(overload.drawText(PointF(), ''), Overload.FunctionEnum.Function1)
 
     def testDrawTextRectIntStr(self):
         overload = Overload()
-        self.assertEqual(overload.drawText(Rect(), 1, Str()), Overload.Function2)
-        self.assertEqual(overload.drawText(Rect(), 1, ''), Overload.Function2)
-        self.assertEqual(overload.drawText(RectF(), 1, Str()), Overload.Function3)
-        self.assertEqual(overload.drawText(RectF(), 1, ''), Overload.Function3)
+        self.assertEqual(overload.drawText(Rect(), 1, Str()), Overload.FunctionEnum.Function2)
+        self.assertEqual(overload.drawText(Rect(), 1, ''), Overload.FunctionEnum.Function2)
+        self.assertEqual(overload.drawText(RectF(), 1, Str()), Overload.FunctionEnum.Function3)
+        self.assertEqual(overload.drawText(RectF(), 1, ''), Overload.FunctionEnum.Function3)
 
     def testDrawTextRectFStrEcho(self):
         overload = Overload()
-        self.assertEqual(overload.drawText(RectF(), Str()), Overload.Function4)
-        self.assertEqual(overload.drawText(RectF(), ''), Overload.Function4)
-        self.assertEqual(overload.drawText(RectF(), Str(), Echo()), Overload.Function4)
-        self.assertEqual(overload.drawText(RectF(), '', Echo()), Overload.Function4)
-        self.assertEqual(overload.drawText(Rect(), Str()), Overload.Function4)
-        self.assertEqual(overload.drawText(Rect(), ''), Overload.Function4)
-        self.assertEqual(overload.drawText(Rect(), Str(), Echo()), Overload.Function4)
-        self.assertEqual(overload.drawText(Rect(), '', Echo()), Overload.Function4)
+        self.assertEqual(overload.drawText(RectF(), Str()), Overload.FunctionEnum.Function4)
+        self.assertEqual(overload.drawText(RectF(), ''), Overload.FunctionEnum.Function4)
+        self.assertEqual(overload.drawText(RectF(), Str(), Echo()), Overload.FunctionEnum.Function4)
+        self.assertEqual(overload.drawText(RectF(), '', Echo()), Overload.FunctionEnum.Function4)
+        self.assertEqual(overload.drawText(Rect(), Str()), Overload.FunctionEnum.Function4)
+        self.assertEqual(overload.drawText(Rect(), ''), Overload.FunctionEnum.Function4)
+        self.assertEqual(overload.drawText(Rect(), Str(), Echo()), Overload.FunctionEnum.Function4)
+        self.assertEqual(overload.drawText(Rect(), '', Echo()), Overload.FunctionEnum.Function4)
 
     def testDrawTextIntIntStr(self):
         overload = Overload()
-        self.assertEqual(overload.drawText(1, 2, Str()), Overload.Function5)
-        self.assertEqual(overload.drawText(1, 2, ''), Overload.Function5)
+        self.assertEqual(overload.drawText(1, 2, Str()), Overload.FunctionEnum.Function5)
+        self.assertEqual(overload.drawText(1, 2, ''), Overload.FunctionEnum.Function5)
 
     def testDrawTextIntIntIntIntStr(self):
         overload = Overload()
-        self.assertEqual(overload.drawText(1, 2, 3, 4, 5, Str()), Overload.Function6)
-        self.assertEqual(overload.drawText(1, 2, 3, 4, 5, ''), Overload.Function6)
+        self.assertEqual(overload.drawText(1, 2, 3, 4, 5, Str()), Overload.FunctionEnum.Function6)
+        self.assertEqual(overload.drawText(1, 2, 3, 4, 5, ''), Overload.FunctionEnum.Function6)
 
     def testDrawText2IntIntIntIntStr(self):
         overload = Overload()
-        self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, Str()), Overload.Function6)
-        self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, ''), Overload.Function6)
-        self.assertEqual(overload.drawText2(1, 2, 3, 4, 5), Overload.Function6)
-        self.assertEqual(overload.drawText2(1, 2, 3, 4), Overload.Function6)
-        self.assertEqual(overload.drawText2(1, 2, 3), Overload.Function6)
+        self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, Str()), Overload.FunctionEnum.Function6)
+        self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, ''), Overload.FunctionEnum.Function6)
+        self.assertEqual(overload.drawText2(1, 2, 3, 4, 5), Overload.FunctionEnum.Function6)
+        self.assertEqual(overload.drawText2(1, 2, 3, 4), Overload.FunctionEnum.Function6)
+        self.assertEqual(overload.drawText2(1, 2, 3), Overload.FunctionEnum.Function6)
 
     def testDrawText3(self):
         overload = Overload()
-        self.assertEqual(overload.drawText3(Str(), Str(), Str()), Overload.Function0)
-        self.assertEqual(overload.drawText3('', '', ''), Overload.Function0)
-        self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.Function1)
-        self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.Function1)
+        self.assertEqual(overload.drawText3(Str(), Str(), Str()), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.drawText3('', '', ''), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.FunctionEnum.Function1)
+        self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.FunctionEnum.Function1)
 
     def testDrawText3Exception(self):
         overload = Overload()
@@ -138,37 +142,39 @@ class OverloadTest(unittest.TestCase):
 
     def testDrawText4(self):
         overload = Overload()
-        self.assertEqual(overload.drawText4(1, 2, 3), Overload.Function0)
-        self.assertEqual(overload.drawText4(1, 2, 3, 4, 5), Overload.Function1)
+        self.assertEqual(overload.drawText4(1, 2, 3), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.drawText4(1, 2, 3, 4, 5), Overload.FunctionEnum.Function1)
 
     def testAcceptSequence(self):
         # Overload.acceptSequence()
         overload = Overload()
-        self.assertEqual(overload.acceptSequence(), Overload.Function0)
+        self.assertEqual(overload.acceptSequence(), Overload.FunctionEnum.Function0)
 
     def testAcceptSequenceIntInt(self):
         # Overload.acceptSequence(int,int)
         overload = Overload()
-        self.assertEqual(overload.acceptSequence(1, 2), Overload.Function1)
+        self.assertEqual(overload.acceptSequence(1, 2), Overload.FunctionEnum.Function1)
 
     def testAcceptSequenceStrParamEnum(self):
         # Overload.acceptSequence(Str,Overload::ParamEnum)
         overload = Overload()
-        self.assertEqual(overload.acceptSequence(''), Overload.Function2)
-        self.assertEqual(overload.acceptSequence('', Overload.Param0), Overload.Function2)
-        self.assertEqual(overload.acceptSequence(Str('')), Overload.Function2)
-        self.assertEqual(overload.acceptSequence(Str(''), Overload.Param0), Overload.Function2)
+        self.assertEqual(overload.acceptSequence(''), Overload.FunctionEnum.Function2)
+        self.assertEqual(overload.acceptSequence('', Overload.ParamEnum.Param0),
+                         Overload.FunctionEnum.Function2)
+        self.assertEqual(overload.acceptSequence(Str('')), Overload.FunctionEnum.Function2)
+        self.assertEqual(overload.acceptSequence(Str(''), Overload.ParamEnum.Param0),
+                         Overload.FunctionEnum.Function2)
 
     def testAcceptSequenceSize(self):
         # Overload.acceptSequence(Size)
         overload = Overload()
-        self.assertEqual(overload.acceptSequence(Size()), Overload.Function3)
+        self.assertEqual(overload.acceptSequence(Size()), Overload.FunctionEnum.Function3)
 
     def testAcceptSequenceStringList(self):
         # Overload.acceptSequence(const char**)
         overload = Overload()
         strings = ['line 1', 'line 2']
-        self.assertEqual(overload.acceptSequence(strings), Overload.Function4)
+        self.assertEqual(overload.acceptSequence(strings), Overload.FunctionEnum.Function4)
         args = (['line 1', 2], )
         result = raisesWithErrorMessage(overload.acceptSequence, args,
                                         TypeError, 'The argument must be a sequence of strings.')
@@ -178,11 +184,11 @@ class OverloadTest(unittest.TestCase):
         # Overload.acceptSequence(void*)
         overload = Overload()
 
-        class Foo(object):
+        class Foo:
             pass
 
         foo = Foo()
-        self.assertEqual(overload.acceptSequence(foo), Overload.Function5)
+        self.assertEqual(overload.acceptSequence(foo), Overload.FunctionEnum.Function5)
 
 
 if __name__ == '__main__':
index 269b9729912f5b5458c5a673e16d7fe8a9dbf0ca..07dee9623d22b75398037591138f329ec672686c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -18,26 +19,30 @@ class OverloadTest(unittest.TestCase):
 
     def testNoArgument(self):
         overload = Overload()
-        self.assertEqual(overload.strBufferOverloads(), Overload.Function2)
+        self.assertEqual(overload.strBufferOverloads(), Overload.FunctionEnum.Function2)
 
     def testStrArgument(self):
         overload = Overload()
-        self.assertEqual(overload.strBufferOverloads(Str('')), Overload.Function0)
-        self.assertEqual(overload.strBufferOverloads(Str(''), ''), Overload.Function0)
-        self.assertEqual(overload.strBufferOverloads(Str(''), '', False), Overload.Function0)
+        self.assertEqual(overload.strBufferOverloads(Str('')), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.strBufferOverloads(Str(''), ''), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.strBufferOverloads(Str(''), '', False),
+                         Overload.FunctionEnum.Function0)
 
     def testStringArgumentAsStr(self):
         overload = Overload()
-        self.assertEqual(overload.strBufferOverloads('', ''), Overload.Function0)
-        self.assertEqual(overload.strBufferOverloads('', '', False), Overload.Function0)
+        self.assertEqual(overload.strBufferOverloads('', ''), Overload.FunctionEnum.Function0)
+        self.assertEqual(overload.strBufferOverloads('', '', False),
+                         Overload.FunctionEnum.Function0)
 
     def testStringArgumentAsBuffer(self):
         overload = Overload()
-        self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0), Overload.Function1)
+        self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0),
+                         Overload.FunctionEnum.Function1)
 
     def testBufferArgument(self):
         overload = Overload()
-        self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0), Overload.Function1)
+        self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0),
+                         Overload.FunctionEnum.Function1)
 
 
 if __name__ == '__main__':
index 8a55d3ab82af2b21468f7b20f0e20d310da50382..5233571ed86dd710ae20be57c0fadc14d4efc1f1 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Wrapper validity tests for arguments.'''
 
index 25c6fea266d1e5c761c7abe03d7e36299aefeeb0..5943c443737ed4de751f447a831dc0ddb3cf1c2b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for destroy a child object in C++'''
 
index 3ae1868150c66c661e011113ebe8ce430868b396..93df5177351215956ff3773519818f484adc3666 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for deleting a child object in python'''
 
index 8f654639c7cacf41179ed2895d3bba9c88a49517..758ba83511f57f59400e9b36c8c003c045942bba 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for destroying the parent'''
 
index 37b7591e46551e9401dc9c1efa40a9fddcf45dfc..4c6baf00c4691a95c015e8ee8d3e5893ee5ae2b4 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Ownership tests for cases of invalidation of Python wrapper after use.'''
 
@@ -65,29 +66,29 @@ class OwnershipInvalidateAfterUseTest(unittest.TestCase):
         '''In ObjectType.event(Event*) the wrapper object created for Event
            must me marked as invalid after the method is called.'''
         eot = ExtObjectType()
-        eot.causeEvent(Event.SOME_EVENT)
-        self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT)
+        eot.causeEvent(Event.EventType.SOME_EVENT)
+        self.assertEqual(eot.type_of_last_event, Event.EventType.SOME_EVENT)
         self.assertRaises(RuntimeError, eot.last_event.eventType)
 
     def testObjectInvalidatedAfterUseAsParameter(self):
         '''Tries to use wrapper invalidated after use as a parameter to another method.'''
         eot = ExtObjectType()
         ot = ObjectType()
-        eot.causeEvent(Event.ANY_EVENT)
-        self.assertEqual(eot.type_of_last_event, Event.ANY_EVENT)
+        eot.causeEvent(Event.EventType.ANY_EVENT)
+        self.assertEqual(eot.type_of_last_event, Event.EventType.ANY_EVENT)
         self.assertRaises(RuntimeError, ot.event, eot.last_event)
 
     def testit(self):
         obj = MyObjectType()
-        obj.causeEvent(Event.BASIC_EVENT)
+        obj.causeEvent(Event.EventType.BASIC_EVENT)
         self.assertFalse(obj.fail)
 
     def testInvalidateAfterUseInDerived(self):
         '''Invalidate was failing in a derived C++ class that also inherited
         other base classes'''
         eot = ExtObjectTypeDerived()
-        eot.causeEvent(Event.SOME_EVENT)
-        self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT)
+        eot.causeEvent(Event.EventType.SOME_EVENT)
+        self.assertEqual(eot.type_of_last_event, Event.EventType.SOME_EVENT)
         self.assertRaises(RuntimeError, eot.last_event.eventType)
 
 
index 77b7c576ceee9d121533c3856e7c0c6d4e379af9..40011a0cb0d49207985a0dd15533ba11ea8d1594 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for invalidating a C++ created child that was already on the care of a parent.'''
 
index 8cbefc30cc7b100d98d1a05526c6a94c93c9a04f..d69fc3f1cfe8d26a69098a1c56eee75238993848 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''The BlackBox class has cases of ownership transference between Python and C++.'''
 
index c721a212c0980c28ceb090492dbbb8bd5efa69d3..8238876af3a9fd602ceb0e70da4fd0a5c86cbe11 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for invalidating a parent of other objects.'''
 
index 304223063ea385aaf4b8f932b4152f7817288a50..ecb291123779641cbe57115e6940293010fc52d0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests for object reparenting.'''
 
index 0e9f08b7250168d7f0489b5128df2e7c731e0bcd..c22d29a1c63ed433ae5943d610ce8537fc1a3742 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''The BlackBox class has cases of ownership transference between C++ and Python.'''
 
index 4bd5c697c54ea24a42a5fa15748078a9f1bd428b..2770dd3a4dce26536536b317c5c09dbd2853a4db 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for std::pair container conversions'''
 
index 106f3bd61d1ce6b9ad90065740853ce0926bb88c..9bfc30728ad02a762a91ca21bc6a071d5b41f373 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for <add-function> with const char* as argument'''
 
@@ -24,7 +25,7 @@ class TestPen(unittest.TestCase):
         self.assertEqual(pen.ctorType(), Pen.EmptyCtor)
 
     def testPenWithEnumConstructor(self):
-        pen = Pen(SampleNamespace.RandomNumber)
+        pen = Pen(SampleNamespace.Option.RandomNumber)
         self.assertEqual(pen.ctorType(), Pen.EnumCtor)
 
     def testPenWithColorConstructor(self):
index f86c0f423596fcd2698d27359e3052868cae70d6..301fe1ab8e2c4d54c43f44bfb56ce38d07f2f5b6 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for Point class'''
 
index 633525a9c0cae69d547aff5b36ea048bfb4b7360..0c131609554aaf38f54cf826e0f40f00ac52dc77 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for a class that holds an arbitraty pointer and is modified to hold an PyObject.'''
 
index 4da1a89c604702e9590b1644998becbe0dc5248a..4bfe125cd896a4081c7e939f676d15042fd656e8 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 pointerprimitivetype_test.py
index 91c58eb1d4334f691770c2b367194e2f5c2c6656..6190dca1fd309af7ac428bfa3f8f4735ac2de3a2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for PointF class'''
 
index 0b9fe2249f910b21be36bc7c4115796645401c35..19042f614fc5ee453aa769718770af9a0c7ee206 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 63040388d11c7d529736f4d4b62ccb3ede33e4c5..df7ee402bc40d9b397529c45f0306f9ebfa80efa 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for a class with only a private constructor.'''
 
index 651f63b15213ec7d924088155dc8ab5a97d1d1d6..df1bd90f4436ee6fb4334a18d31b9971b99ab4d8 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for a class with a private destructor.'''
 
index e4ccf721dfc24533326f8984ceb756fc55a971d9..319a564b45ae043f02244e1bc184f1c74ae97cb1 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for protected methods.'''
 
@@ -215,14 +216,14 @@ class ExtendedProtectedEnumClass(ProtectedEnumClass):
         ProtectedEnumClass.__init__(self)
 
     def protectedEnumMethod(self, value):
-        if value == ProtectedEnumClass.ProtectedItem0:
-            return ProtectedEnumClass.ProtectedItem1
-        return ProtectedEnumClass.ProtectedItem0
+        if value == ProtectedEnumClass.ProtectedEnum.ProtectedItem0:
+            return ProtectedEnumClass.ProtectedEnum.ProtectedItem1
+        return ProtectedEnumClass.ProtectedEnum.ProtectedItem0
 
     def publicEnumMethod(self, value):
-        if value == ProtectedEnumClass.PublicItem0:
-            return ProtectedEnumClass.PublicItem1
-        return ProtectedEnumClass.PublicItem0
+        if value == ProtectedEnumClass.PublicEnum.PublicItem0:
+            return ProtectedEnumClass.PublicEnum.PublicItem1
+        return ProtectedEnumClass.PublicEnum.PublicItem0
 
 
 class ProtectedEnumTest(unittest.TestCase):
@@ -237,68 +238,69 @@ class ProtectedEnumTest(unittest.TestCase):
         '''Calls protected method with protected enum argument.'''
         obj = ProtectedEnumClass()
 
-        self.assertEqual(type(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedEnum)
+        self.assertEqual(type(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),
+                         ProtectedEnumClass.ProtectedEnum)
 
-        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
-                         ProtectedEnumClass.ProtectedItem0)
-        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
-                         ProtectedEnumClass.ProtectedItem1)
-        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
-                         ProtectedEnumClass.ProtectedItem0)
-        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
-                         ProtectedEnumClass.ProtectedItem1)
+        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
+        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1),
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
+        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),  # noqa: E501
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
+        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1),  # noqa: E501
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
 
     def testProtectedMethodWithPublicEnumArgument(self):
         '''Calls protected method with public enum argument.'''
         obj = ProtectedEnumClass()
 
-        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0),
-                         ProtectedEnumClass.PublicItem0)
-        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1),
-                         ProtectedEnumClass.PublicItem1)
+        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+                         ProtectedEnumClass.PublicEnum.PublicItem0)
+        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+                         ProtectedEnumClass.PublicEnum.PublicItem1)
 
-        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0),
-                         ProtectedEnumClass.PublicItem0)
-        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1),
-                         ProtectedEnumClass.PublicItem1)
+        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+                         ProtectedEnumClass.PublicEnum.PublicItem0)
+        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+                         ProtectedEnumClass.PublicEnum.PublicItem1)
 
     def testOverriddenProtectedMethodWithProtectedEnumArgument(self):
         '''Calls overridden protected method with protected enum argument.'''
         obj = ExtendedProtectedEnumClass()
 
-        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
-                         ProtectedEnumClass.ProtectedItem1)
-        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
-                         ProtectedEnumClass.ProtectedItem0)
+        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
+        self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1),
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
 
-        self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedItem0),  # noqa: E501
-                         ProtectedEnumClass.ProtectedItem0)
+        self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedEnum.ProtectedItem0),  # noqa: E501
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
         self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj,
-                         ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem1)
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem1), ProtectedEnumClass.ProtectedEnum.ProtectedItem1)  # noqa: E501
 
-        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
-                         ProtectedEnumClass.ProtectedItem1)
-        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
-                         ProtectedEnumClass.ProtectedItem0)
+        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),  # noqa: E501
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
+        self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1),  # noqa: E501
+                         ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
 
     def testOverriddenProtectedMethodWithPublicEnumArgument(self):
         '''Calls overridden protected method with public enum argument.'''
         obj = ExtendedProtectedEnumClass()
 
-        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0),
-                         ProtectedEnumClass.PublicItem1)
-        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1),
-                         ProtectedEnumClass.PublicItem0)
+        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+                         ProtectedEnumClass.PublicEnum.PublicItem1)
+        self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+                         ProtectedEnumClass.PublicEnum.PublicItem0)
 
-        self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem0),
-                         ProtectedEnumClass.PublicItem0)
-        self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem1),
-                         ProtectedEnumClass.PublicItem1)
+        self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicEnum.PublicItem0),  # noqa: E501
+                         ProtectedEnumClass.PublicEnum.PublicItem0)
+        self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicEnum.PublicItem1),  # noqa: E501
+                         ProtectedEnumClass.PublicEnum.PublicItem1)
 
-        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0),
-                         ProtectedEnumClass.PublicItem1)
-        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1),
-                         ProtectedEnumClass.PublicItem0)
+        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+                         ProtectedEnumClass.PublicEnum.PublicItem1)
+        self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+                         ProtectedEnumClass.PublicEnum.PublicItem0)
 
 
 class ProtectedPropertyTest(unittest.TestCase):
@@ -326,8 +328,8 @@ class ProtectedPropertyTest(unittest.TestCase):
 
     def testProtectedEnumProperty(self):
         '''Writes and reads a protected enum property.'''
-        self.obj.protectedEnumProperty = Event.SOME_EVENT
-        self.assertEqual(self.obj.protectedEnumProperty, Event.SOME_EVENT)
+        self.obj.protectedEnumProperty = Event.EventType.SOME_EVENT
+        self.assertEqual(self.obj.protectedEnumProperty, Event.EventType.SOME_EVENT)
 
     def testProtectedValueTypeProperty(self):
         '''Writes and reads a protected value type property.'''
index d60f9cf355cd297e7e0834cbbc020bd26be2f5d2..d4e4c8316f249dddad215339b16c678c41ff2492 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index ec64c1e31389e6805f647413c5c8f32dfce99d95..f7b4656bb32ac96d7c588317ca752888ab7bee50 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for definition of __str__ method.'''
 
index 65398b5c676455dfc7690ff8fe0aa21abdf480b9..2cee3494772bb59182b3cd187feee34c84837dc8 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 #!/usr/bin/env python
 
index 1d19de9414ed51a92dce48837b517dc14e4415ca..686a2435aee09c46532a87c1cee2ed1c01a66e83 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for a function that could receive a NULL pointer in a '[const] char*' parameter.'''
 
index 1b6dd3a7a331d6313282b5008e41256902ec2387..ab5387ccb86034308aa4f7aa2c0250f76d214461 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for methods that receive references to objects.'''
 
index 942c7ea290c2e6983b31beb50346cce408550257..3c614ba188b9552e45861048377afb65272b01a6 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for a reference to pointer argument type.'''
 
index b08438ef35dfe642f9bc36529ea30904f3043e6d..559d69f3e01d85466061ba4a077679dfb68e3f88 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for renaming using target-lang-name attribute.'''
 
@@ -31,7 +32,7 @@ class RenamingTest(unittest.TestCase):
         rename_user = RenamedUser()
         rename_user.useRenamedValue(renamed_value)
         actual_signature = str(get_signature(rename_user.useRenamedValue))
-        self.assertTrue(re.match(r"^\(self,\s*?v:\s*?sample.RenamedValue\)\s*?->\s*?None$",
+        self.assertTrue(re.match(r"^\(self,\s*?v:\s*?sample.RenamedValue(,\s*?/)?\)\s*?->\s*?None$",
                                  actual_signature))
 
 
index 2c4f07c653645fa8098d1103020b7641b2d80466..92341e01503a917b4be491e9cecd3876d108571b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for functions that could return a NULL pointer.'''
 
index 3146d0fafd9df4acd028e93d4615a3d1d1496c5e..decd8f3b9c75462f438e9570f8691d2115a12e12 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 19b2f708d451d0fc4830e0791a4d3d5e8033a06e..2e11bebe41f92024a15eb61cb295a64d6bdfb919 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for libsample bindings module'''
 
index 55c894a354677452e49d3e13a0e149b296b061eb..6b6dccac77ea50b3a0a359c4911a22ffdd6a3478 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for SimpleFile class'''
 
index 069ce59b39dbad9fd7f526f156d8d84c5bad0a35..d6561faf04b29d7d835525401bcd7a08cf30bf8f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for operator overloads on Size class'''
 
index a1538796a5c126d917e360fc2b0403b9aa8cd84e..2c28124983129224a616e3c2524b73f04e315fd0 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for snake case generation'''
 
index cf08892998a0d0da6d420323dc494504ad1f2db6..ac05375fd586323fb8626f943f381a057a374863 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for overloads involving static and non-static versions of a method.'''
 
index 0caa9764d8ecb666e750573bb25694cb9b91540d..2de954ff2ca7bae62a55f55997c4436d0f80cd26 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for StdComplex class'''
 
index c06fd64282eb48655acf5cd3171a5a90577da5c9..a30a79e96df058c72e3d72f8a43e01e11a65d8c9 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for a method that receives a reference to class that is implicitly
    convertible from a Python native type.'''
index 2bfb80b6722600d8ca46832cffcc386989af1aa7..74e3b5388d313635d65d53f2133e8a5e1a024a9c 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for StrList class that inherits from std::list<Str>.'''
 
@@ -23,7 +24,7 @@ class StrListTest(unittest.TestCase):
         '''StrList constructor receives no parameter.'''
         sl = StrList()
         self.assertEqual(len(sl), 0)
-        self.assertEqual(sl.constructorUsed(), StrList.NoParamsCtor)
+        self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.NoParamsCtor)
 
     def testStrListCtor_Str(self):
         '''StrList constructor receives a Str object.'''
@@ -31,7 +32,7 @@ class StrListTest(unittest.TestCase):
         sl = StrList(s)
         self.assertEqual(len(sl), 1)
         self.assertEqual(sl[0], s)
-        self.assertEqual(sl.constructorUsed(), StrList.StrCtor)
+        self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.StrCtor)
 
     def testStrListCtor_PythonString(self):
         '''StrList constructor receives a Python string.'''
@@ -39,7 +40,7 @@ class StrListTest(unittest.TestCase):
         sl = StrList(s)
         self.assertEqual(len(sl), 1)
         self.assertEqual(sl[0], s)
-        self.assertEqual(sl.constructorUsed(), StrList.StrCtor)
+        self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.StrCtor)
 
     def testStrListCtor_StrList(self):
         '''StrList constructor receives a StrList object.'''
@@ -47,7 +48,7 @@ class StrListTest(unittest.TestCase):
         sl2 = StrList(sl1)
         #self.assertEqual(len(sl1), len(sl2))
         #self.assertEqual(sl1, sl2)
-        self.assertEqual(sl2.constructorUsed(), StrList.CopyCtor)
+        self.assertEqual(sl2.constructorUsed(), StrList.CtorEnum.CopyCtor)
 
     def testStrListCtor_ListOfStrs(self):
         '''StrList constructor receives a Python list of Str objects.'''
@@ -55,7 +56,7 @@ class StrListTest(unittest.TestCase):
         sl = StrList(strs)
         self.assertEqual(len(sl), len(strs))
         self.assertEqual(sl, strs)
-        self.assertEqual(sl.constructorUsed(), StrList.ListOfStrCtor)
+        self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.ListOfStrCtor)
 
     def testStrListCtor_MixedListOfStrsAndPythonStrings(self):
         '''StrList constructor receives a Python list of mixed Str objects and Python strings.'''
@@ -63,7 +64,7 @@ class StrListTest(unittest.TestCase):
         sl = StrList(strs)
         self.assertEqual(len(sl), len(strs))
         self.assertEqual(sl, strs)
-        self.assertEqual(sl.constructorUsed(), StrList.ListOfStrCtor)
+        self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.ListOfStrCtor)
 
     def testCompareStrListWithTupleOfStrs(self):
         '''Compares StrList with a Python tuple of Str objects.'''
index 11279c7ec0d0915b191ef3aba7bb163aa8580eab..013b9979e814f4d88f6a36f43a4550696140bd1a 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -19,8 +20,8 @@ from sample import Photon
 
 class TemplateInheritingClassTest(unittest.TestCase):
     def testClassBasics(self):
-        self.assertEqual(Photon.ValueIdentity.classType(), Photon.IdentityType)
-        self.assertEqual(Photon.ValueDuplicator.classType(), Photon.DuplicatorType)
+        self.assertEqual(Photon.ValueIdentity.classType(), Photon.ClassType.IdentityType)
+        self.assertEqual(Photon.ValueDuplicator.classType(), Photon.ClassType.DuplicatorType)
 
     def testInstanceBasics(self):
         value = 123
index 6283a6744b12d21e557ad5d3bea71f6d571d6356..cc247ac1ef50f4c2cba943f34ac986d6dccef00f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for constructor and method signature decisor on Time class.'''
 
@@ -73,7 +74,7 @@ class TimeTest(unittest.TestCase):
         '''Method without parameters: Time.somethingCompletelyDifferent()'''
         time = Time()
         result = time.somethingCompletelyDifferent()
-        self.assertEqual(result, Time.ZeroArgs)
+        self.assertEqual(result, Time.NumArgs.ZeroArgs)
 
     def testMethodWithAllParamers(self):
         '''Method with all parameters:
@@ -84,25 +85,26 @@ class TimeTest(unittest.TestCase):
         time = Time()
         obj = ObjectType()
         result = time.somethingCompletelyDifferent(1, 2, ImplicitConv(2), obj)
-        self.assertEqual(result, Time.FourArgs)
+        self.assertEqual(result, Time.NumArgs.FourArgs)
 
     def testMethodWithThreeParamers(self):
         '''Method with 3 parameters: Time.somethingCompletelyDifferent(...)'''
         time = Time()
-        result = time.somethingCompletelyDifferent(1, 2, ImplicitConv(ImplicitConv.CtorOne))
-        self.assertEqual(result, Time.ThreeArgs)
+        result = time.somethingCompletelyDifferent(1, 2,
+                                                   ImplicitConv(ImplicitConv.CtorEnum.CtorOne))
+        self.assertEqual(result, Time.NumArgs.ThreeArgs)
 
     def testMethodWithTwoParamers(self):
         '''Method with 2 parameters: Time.somethingCompletelyDifferent(...)'''
         time = Time()
         result = time.somethingCompletelyDifferent(1, 2)
-        self.assertEqual(result, Time.TwoArgs)
+        self.assertEqual(result, Time.NumArgs.TwoArgs)
 
     def testMethodWithThreeParamersAndImplicitConversion(self):
         '''Method with 3 parameters, the last one triggers an implicit conversion.'''
         time = Time()
-        result = time.somethingCompletelyDifferent(1, 2, ImplicitConv.CtorOne)
-        self.assertEqual(result, Time.ThreeArgs)
+        result = time.somethingCompletelyDifferent(1, 2, ImplicitConv.CtorEnum.CtorOne)
+        self.assertEqual(result, Time.NumArgs.ThreeArgs)
 
     # PYSIDE-1436: These tests crash at shutdown due to `assert(Not)?Equal`.
     def testCompareWithPythonTime(self):
index 7dfd18a4ad671ce4e25134f761cc5e9a09b0ee8c..e9897c8f8b221342dd1b0d24bca48ec9e4f790b2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for argument modification with more than nine arguments.'''
 
index 987ba6dfda19006eef60e536943d4582a5c5932b..049b17f652f84ef596bdd34d7ffe4c8a141823be 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Tests various usages of the type converters.'''
 
index ce881e80222bd0c3d60e78b606806a48df1d9d7b..468095af7e8186ee8a6e251ca5fbab6654308aee 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test deallocation of type extended in Python.'''
 
index ab8e535b5c3a97ee52d0f4fa3c3a8a69eff29278..36ff7ddf465759e6a6cce7e859ca940c445c4f92 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index e315e599e2a81e9a79647c311c3ae1a4ade6d1f8..481d06ff682809f481c823870949993a0cd2a2e2 100644 (file)
                 <add-conversion type="PySequence">
                 %OUTTYPE&amp; list = %out;
                 Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0));
-                for (int i = 0; i &lt; PySequence_Fast_GET_SIZE(seq.object()); i++) {
-                    PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i);
+                for (Py_ssize_t i = 0; i &lt; PySequence_Size(seq.object()); i++) {
+                    Shiboken::AutoDecRef pyItem(PySequence_GetItem(seq.object(), i));
                     PStr cppItem = %CONVERTTOCPP[PStr](pyItem);
                     list.push_back(cppItem);
                 }
         PyObject* result = PyTuple_New(size);
         for (int i = 0; i &lt; size; ++i) {
             Shiboken::Conversions::SpecificConverter converter(typeName[i]);
-            PyTuple_SET_ITEM(result, i, converter.toPython(values[i]));
+            PyTuple_SetItem(result, i, converter.toPython(values[i]));
         }
         return result;
     }
 
     <template name="boolptr_fix_end">
         PyObject* _tuple_ = PyTuple_New(2);
-        PyTuple_SET_ITEM(_tuple_, 0, %PYARG_0);
+        PyTuple_SetItem(_tuple_, 0, %PYARG_0);
         PyObject* _item_ = %CONVERTTOPYTHON[bool](__ok__);
-        PyTuple_SET_ITEM(_tuple_, 1, _item_);
+        PyTuple_SetItem(_tuple_, 1, _item_);
         %PYARG_0 = _tuple_;
     </template>
 
     <template name="return_4_arguments_as_tuple">
         %PYARG_0 = PyTuple_New(4);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
-        PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
-        PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
+        PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
+        PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
     </template>
 
     <template name="return_5_arguments_as_tuple">
         %PYARG_0 = PyTuple_New(5);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
-        PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
-        PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
-        PyTuple_SET_ITEM(%PYARG_0, 4, %CONVERTTOPYTHON[%ARG5_TYPE](%5));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
+        PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
+        PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
+        PyTuple_SetItem(%PYARG_0, 4, %CONVERTTOPYTHON[%ARG5_TYPE](%5));
     </template>
 
     <template name="return_none">
                 <replace-type modified-type="PySequence" />
                 <conversion-rule class="native">
                 Shiboken::AutoArrayPointer&lt;Point&gt; %out(%1);
-                for (Py_ssize_t i = 0; i &lt; %1; ++i)
-                    %out[i] = %CONVERTTOCPP[Point](PySequence_Fast_GET_ITEM(%PYARG_1, i));
+                for (Py_ssize_t i = 0; i &lt; %1; ++i) {
+                    Shiboken::AutoDecRef _obj(PySequence_GetItem(%PYARG_1, i));
+                    %out[i] = %CONVERTTOCPP[Point](_obj);
+                }
                 </conversion-rule>
             </modify-argument>
         </modify-function>
         </template>
         <template name="differenceOfPointCoordinates_returnTarget">
         %PYARG_0 = PyTuple_New(2);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[bool](*%2));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[bool](*%2));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
         </template>
         <modify-function signature="differenceOfPointCoordinates(const Point*, bool*)">
             <modify-argument index="2">
           %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(&amp;tmpObject);
           %END_ALLOW_THREADS
           %PYARG_0 = PyTuple_New(2);
-          PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
-          PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[ObjectType*](tmpObject));
+          PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+          PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[ObjectType*](tmpObject));
           </inject-code>
         </modify-function>
         <modify-function signature="passOddBool(OddBool)" rename="invertBoolean">
             <inject-code class="target" position="end">
                 %PYARG_0 = PyTuple_New(2);
                 PyObject* _item_ = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
-                PyTuple_SET_ITEM(%PYARG_0, 0, _item_);
+                PyTuple_SetItem(%PYARG_0, 0, _item_);
                 _item_ = %CONVERTTOPYTHON[Str*](_str_arg_);
-                PyTuple_SET_ITEM(%PYARG_0, 1, _item_);
+                PyTuple_SetItem(%PYARG_0, 1, _item_);
             </inject-code>
         </modify-function>
         <modify-function signature="callCreateStr(const char*, Str*&amp;)">
             <inject-code class="target" position="end">
                 %PYARG_0 = PyTuple_New(2);
                 PyObject* _item_ = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
-                PyTuple_SET_ITEM(%PYARG_0, 0, _item_);
+                PyTuple_SetItem(%PYARG_0, 0, _item_);
                 _item_ = %CONVERTTOPYTHON[Str*](_str_arg_);
-                PyTuple_SET_ITEM(%PYARG_0, 1, _item_);
+                PyTuple_SetItem(%PYARG_0, 1, _item_);
             </inject-code>
         </modify-function>
         <template name="fix_int*,int*,int*,int*">
         %CPPSELF->::%TYPE::%FUNCTION_NAME(&amp;a0, &amp;a1, &amp;a2, &amp;a3);
         %END_ALLOW_THREADS
         %PYARG_0 = PyTuple_New(4);
-        PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](a0));
-        PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](a1));
-        PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](a2));
-        PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[int](a3));
+        PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](a0));
+        PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](a1));
+        PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](a2));
+        PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[int](a3));
         </template>
         <template name="fix_native_return_int*,int*,int*,int*">
             PyObject* _obj = %PYARG_0;
+            Shiboken::AutoDecRef _obj0(PySequence_GetItem(_obj, 0));
+            Shiboken::AutoDecRef _obj1(PySequence_GetItem(_obj, 1));
+            Shiboken::AutoDecRef _obj2(PySequence_GetItem(_obj, 2));
+            Shiboken::AutoDecRef _obj3(PySequence_GetItem(_obj, 3));
             if (!PySequence_Check(_obj)
-                || PySequence_Fast_GET_SIZE(_obj) != 4
-                || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 0))
-                || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 1))
-                || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 2))
-                || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 3))) {
+                || PySequence_Size(_obj) != 4
+                || !PyNumber_Check(_obj0)
+                || !PyNumber_Check(_obj1)
+                || !PyNumber_Check(_obj2)
+                || !PyNumber_Check(_obj3)) {
                 PyErr_SetString(PyExc_TypeError, "Sequence of 4 numbers expected");
             } else {
-                *%1 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 0));
-                *%2 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 1));
-                *%3 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 2));
-                *%4 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 3));
+                *%1 = %CONVERTTOCPP[int](_obj0);
+                *%2 = %CONVERTTOCPP[int](_obj1);
+                *%3 = %CONVERTTOCPP[int](_obj2);
+                *%4 = %CONVERTTOCPP[int](_obj3);
             }
         </template>
         <modify-function signature="getMargins(int*,int*,int*,int*)const">
             <modify-argument index="1">
                 <remove-argument/>
                 <conversion-rule class="native">
-                  const auto %out = PySequence_Size(%PYARG_1);
+                  const Py_ssize_t %out = PySequence_Size(%PYARG_1);
                 </conversion-rule>
             </modify-argument>
             <modify-argument index="2">
                 <replace-type modified-type="PySequence"/>
                 <conversion-rule class="native">
-                const auto numItems = PySequence_Size(%PYARG_1);
+                const Py_ssize_t numItems = PySequence_Size(%PYARG_1);
                 Shiboken::AutoArrayPointer&lt;int&gt; %out(numItems);
                 for (Py_ssize_t i = 0; i &lt; numItems; ++i) {
-                    if (%CHECKTYPE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i)))
-                        %out[i] = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(%PYARG_1, i));
-                    else if (%ISCONVERTIBLE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i)))
+                    Shiboken::AutoDecRef _obj(PySequence_GetItem(%PYARG_1, i));
+                    if (%CHECKTYPE[int](_obj))
+                        %out[i] = %CONVERTTOCPP[int](_obj);
+                    else if (%ISCONVERTIBLE[int](_obj))
                         %out[i] = -1;
                     else
                         %out[i] = -2;
                 <replace-type modified-type="PySequence"/>
             </modify-argument>
             <inject-code class="target" position="beginning">
-            int numItems = PySequence_Size(%PYARG_1);
+            Py_ssize_t numItems = PySequence_Size(%PYARG_1);
             int *cppItems = new int[numItems];
-            for (int i = 0; i &lt; numItems; i++)
-                cppItems[i] = %CONVERTTOCPP[int](PySequence_GetItem(%PYARG_1, i));
+            for (int i = 0; i &lt; numItems; i++) {
+                Shiboken::AutoDecRef _obj(PySequence_GetItem(%PYARG_1, i));
+                cppItems[i] = %CONVERTTOCPP[int](_obj);
+            }
             %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(numItems, cppItems);
             %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
             delete[] cppItems;
     </object-type>
 
     <value-type name="Point">
-        <add-function signature="__str__" return-type="PyObject*">
+        <add-function signature="__str__" return-type="str">
             <inject-code class="target" position="beginning">
             int x1 = (int) %CPPSELF.x();
             int x2 = ((int) (%CPPSELF.x() * 100)) - (x1 * 100);
             %PYARG_0 = Shiboken::String::fromFormat("Point(%d.%d, %d.%d)", x1, x2, y1, y2);
             </inject-code>
         </add-function>
-        <add-function signature="__repr__" return-type="PyObject*">
+        <add-function signature="__repr__" return-type="str">
             <inject-code class="target" position="beginning">
             int x1 = (int) %CPPSELF.x();
             int x2 = ((int) (%CPPSELF.x() * 10)) - (x1 * 10);
             </inject-code>
         </add-function>
 
-        <add-function signature="__reduce__" return-type="PyObject*">
+        <add-function signature="__reduce__" return-type="str">
             <inject-code class="target" position="beginning">
             PyObject* type = PyObject_Type(%PYSELF);
             PyObject* args = NULL;
     </value-type>
 
     <value-type name="PointF">
-        <add-function signature="__str__" return-type="PyObject*">
+        <add-function signature="__str__" return-type="str">
             <inject-code class="target" position="beginning">
             int x1 = (int) %CPPSELF.x();
             int x2 = ((int) (%CPPSELF.x() * 100)) - (x1 * 100);
             %PYARG_0 = Shiboken::String::fromFormat("PointF(%d.%d, %d.%d)", x1, x2, y1, y2);
             </inject-code>
         </add-function>
-        <add-function signature="__repr__" return-type="PyObject*">
+        <add-function signature="__repr__" return-type="str">
             <inject-code class="target" position="beginning">
             int x1 = (int) %CPPSELF.x();
             int x2 = ((int) (%CPPSELF.x() * 10)) - (x1 * 10);
             </inject-code>
         </add-function>
 
-        <add-function signature="__reduce__" return-type="PyObject*">
+        <add-function signature="__reduce__" return-type="str">
             <inject-code class="target" position="beginning">
             PyObject *type = PyObject_Type(%PYSELF);
             PyObject *args = NULL;
                 <conversion-rule class="native">
                 {
                     Shiboken::AutoDecRef strList(PySequence_Fast(%PYARG_1, "The argument must be a sequence."));
-                    int lineCount = PySequence_Fast_GET_SIZE(strList.object());
+                    const Py_ssize_t lineCount = PySequence_Size(strList.object());
                     for (int line = 0; line &lt; lineCount; ++line) {
-                        if (!Shiboken::String::check(PySequence_Fast_GET_ITEM(strList.object(), line))) {
+                        Shiboken::AutoDecRef _obj(PySequence_GetItem(strList.object(), line));
+                        bool isString = Shiboken::String::check(_obj);
+                        if (!isString) {
                             PyErr_SetString(PyExc_TypeError, "The argument must be a sequence of strings.");
                             break;
                         }
     </value-type>
 
     <value-type name="Str" hash-function="strHash">
-        <add-function signature="__str__" return-type="PyObject*">
+        <add-function signature="__str__" return-type="str">
             <inject-code class="target" position="beginning">
                 %PYARG_0 = Shiboken::String::fromCString(%CPPSELF.cstring());
             </inject-code>
         </add-function>
-        <add-function signature="__len__" >
+        <add-function signature="__len__" return-type="int">
             <inject-code class="target" position="end">
                 return %CPPSELF.size();
             </inject-code>
                 Py_INCREF(data);
             }
 
-            %0 = new %TYPE(PyBytes_AsString(data), PyBytes_GET_SIZE(data));
+            %0 = new %TYPE(PyBytes_AsString(data), PyBytes_Size(data));
             Py_DECREF(data);
             </inject-code>
         </modify-function>
             Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
             if (!data.isNull()) {
                 ByteArray ba(*%CPPSELF);
-                ba.append(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+                ba.append(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
                 %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
             }
             </inject-code>
             <inject-code>
             Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
             if (!data.isNull()) {
-                ByteArray ba(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+                ByteArray ba(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
                 ba.append(*%CPPSELF);
                 %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
             }
         </add-function>
         <add-function signature="operator+(PyBytes,ByteArray)">
             <inject-code>
-                ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1));
+                ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1));
                 ba = ba + *%CPPSELF;
                 %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
             </inject-code>
         </add-function>
         <add-function signature="operator+(PyBytes)">
             <inject-code>
-                ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1));
+                ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1));
                 ba.append(*%CPPSELF);
                 %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
             </inject-code>
         </add-function>
-        <add-function signature="__repr__" return-type="PyObject*">
+        <add-function signature="__repr__" return-type="str">
             <inject-code class="target" position="beginning">
             ByteArray b(Py_TYPE(%PYSELF)->tp_name);
             PyObject* aux = Shiboken::String::fromStringAndSize(%CPPSELF.data(), %CPPSELF.size());
                 aux = tmp;
             }
             b += "('";
-            b += ByteArray(PyBytes_AS_STRING(aux), PyBytes_GET_SIZE(aux));
+            b += ByteArray(PyBytes_AsString(aux), PyBytes_Size(aux));
             b += "')";
             %PYARG_0 = Shiboken::String::fromStringAndSize(b.data(), b.size());
             </inject-code>
             </inject-code>
         </add-function>
 
-        <add-function signature="__len__">
+        <add-function signature="__len__" return-type="int">
             <inject-code class="target" position="beginning">
             return %CPPSELF.size();
             </inject-code>
 
     <value-type name="StrList">
         <enum-type name="CtorEnum"/>
-        <add-function signature="__len__" >
+        <add-function signature="__len__" return-type="int">
             <inject-code class="target" position="end">
                 return %CPPSELF.size();
             </inject-code>
 
     <add-function signature="countVarargs(int, ...)" return-type="int">
         <inject-code class="target" position="beginning">
-        %RETURN_TYPE %0 = PyTuple_GET_SIZE(%PYARG_2);
+        %RETURN_TYPE %0 = PyTuple_Size(%PYARG_2);
         %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
         </inject-code>
     </add-function>
index f7f5342ee835f66406c5b38fcefc0fa23c4380bf..76047e0ddb3e24d59cfce61a611939f6b35e87e2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for a class that holds a void pointer.'''
 
index 2a7e5cac7a32ffd7c743e49cf247e9922a2a7497..822ee0d3b810458819e8ec6bd1cbb5f2c7801edc 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for ...'''
 
index 45d4095b69ccbe9449ccff7d40c283ce915f37f4..4632e5e04d3b5d3017c7b1360550f73bf3866336 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for user added constructors'''
 
index 6be870269c6115c0fb13da9fc9b190ce8e281f21..14a970297e25aae16b727cbdb96a09f5bc8e197f 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for virtual destructor.'''
 
index 52dc66c907334ba754f0c6a21e18d9369e23bc78..b5896f3a5a583fe6f9c0802762d44d6be90c2cd5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test cases for virtual methods.'''
 
index becdf7423807af4da4f6a9df4922514d7c4bad81..12c2f3abbfa77fe01761d619467598bddb306687 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 186cb473eb29f0e2dfc3a8a5ce91dc52a093d040..f1f2a1182db50aab243656353a461996fee64ea2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test case for a class that holds a void pointer.'''
 
index 01c6d58d5e417b4f8524d2635f8a41d65e0c82f5..645174a8f333503847202ed422bc0f39c88751d5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 '''Test weakref support'''
 
index dfc962db93abd7dc866f2861bc09b55e107ca746..2d8b69bc47473a129be2ed6e2a2c94a4e327cf1b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index 3ec940f2e682c02ca4eb92707730e8dd129e25cb..46c98dadfd4948a11cd66bdc8b7e853c2b7bc8b6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 
 import os
@@ -28,7 +29,6 @@ def get_build_dir():
         look_for = Path("testing")
         here = Path(__file__).resolve().parent
         while here / look_for not in here.iterdir():
-            import pprint
             parent = here.parent
             if parent == here:
                 raise SystemError(look_for + " not found!")
@@ -68,11 +68,8 @@ def add_python_dirs(python_dirs):
 def add_lib_dirs(lib_dirs):
     """Add directories to the platform's library path."""
     if sys.platform == 'win32':
-        if sys.version_info >= (3, 8, 0):
-            for lib_dir in lib_dirs:
-                os.add_dll_directory(lib_dir)
-        else:
-            _prepend_path_var('PATH', lib_dirs)
+        for lib_dir in lib_dirs:
+            os.add_dll_directory(lib_dir)
     else:
         _prepend_path_var('LD_LIBRARY_PATH', lib_dirs)
 
@@ -90,7 +87,7 @@ def shiboken_paths(include_shiboken_tests=False):
     shiboken_dir = Path(get_build_dir()) / 'shiboken6'
     lib_dirs = [os.fspath(shiboken_dir / 'libshiboken')]
     if include_shiboken_tests:
-        shiboken_test_dir = shiboken_dir /'tests'
+        shiboken_test_dir = shiboken_dir / 'tests'
         for module in ['minimal', 'sample', 'smart', 'other']:
             module_dir = shiboken_test_dir / f"{module}binding"
             python_dirs.append(os.fspath(module_dir))
index 14fe6a2d17c879be0ead642218ca8f958c8496e0..8b0d429d501e6c7e6b09f89c4a6f05948e964288 100644 (file)
@@ -1,10 +1,10 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 
 def objectFullname(t):
-    # '__qualname__' for Python 2 does exist for PySide types, only.
     name = getattr(t, "__qualname__", t.__name__)
     module = t.__module__
     if module is None or module == str.__class__.__module__:
index 9f9f8f5a4d84d09fab5b7e2063d21e33ea847ee9..fe12a5e65d49d319fc4631076fe93c8a8c6df94e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index c10d7ab6b4c0f9d5d13abaf0bdab05f2b78888fb..02c4e65960fbec67b2dfda1f5e0b16cef060afdd 100644 (file)
@@ -18,6 +18,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/smart/smart_integer2_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/smart/sharedptr_integer2_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrtestbench_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrvirtualmethodtester_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_double_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_integer_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_int_wrapper.cpp
 ${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_std_string_wrapper.cpp
@@ -37,6 +38,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/smart-binding.txt.in"
                "${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt" @ONLY)
 
 shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+    SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
 
 add_custom_command(
     OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
@@ -45,6 +49,7 @@ add_custom_command(
         ${tool_wrapper}
         $<TARGET_FILE:Shiboken6::shiboken6>
         --project-file=${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt
+        ${UNOPTIMIZE}
         ${GENERATOR_EXTRA_FLAGS}
     DEPENDS ${smart_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
index 8d4272558eb010070553af2a3cae291934ebe0ff..64267fba7459fc8e02d838882eb409020f1afaf5 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import gc
 import os
@@ -204,7 +205,7 @@ class SmartPointerTests(unittest.TestCase):
         self.assertEqual(objCount(), 10)
 
         # clear and delete all objects in the list
-        del ptrToObjList[:]  # Python 2.7 lists have no clear method
+        ptrToObjList.clear()
         # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
         gc.collect()
         self.assertEqual(len(ptrToObjList), 0)
index bee5735480bb9f6d60bd51acd4c48a71231d2608..10e72f125797c91f0b074ae50e2a21edba0c14d2 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -41,6 +42,8 @@ class StdOptionalTests(unittest.TestCase):
         ci = std.optional_int(43)
         self.assertEqual(ci.value(), 43)
 
+    @unittest.skipIf(True, """PYSIDE-2854, T &std::optional::value() does not work/
+                              returns self (colocated).""")
     def testInteger(self):
         b = StdOptionalTestBench()
         i = b.optionalInteger()
index ddaab43f5fc0b4d718b03453ce8d641396b1c805..a46dd2b24ab6a729ab7c7cd90aa4529f4583aacb 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
@@ -10,7 +11,7 @@ from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from shiboken_paths import init_paths
 init_paths()
-from smart import Integer, StdSharedPtrTestBench, StdSharedPtrVirtualMethodTester, std
+from smart import Integer, StdDoublePtr, StdSharedPtrTestBench, StdSharedPtrVirtualMethodTester, std
 
 
 def call_func_on_ptr(ptr):
@@ -52,6 +53,15 @@ class StdSharedPtrTests(unittest.TestCase):
         ip = std.StdIntPtr(42)
         StdSharedPtrTestBench.printInt(ip)
 
+    def testDouble(self):
+        np = StdSharedPtrTestBench.createNullDouble()
+        StdSharedPtrTestBench.printDouble(np)
+        self.assertFalse(np)
+        p = StdSharedPtrTestBench.createDouble(67)
+        StdSharedPtrTestBench.printDouble(p)
+        dp = StdDoublePtr(42)
+        StdSharedPtrTestBench.printDouble(dp)
+
     def testString(self):
         np = StdSharedPtrTestBench.createNullString()
         StdSharedPtrTestBench.printString(np)
index 9c7ef2f01c90f50076cdb2395e950fd37781caea..8e1ae80d9589d41668a92abeef25d55b805f955b 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import os
 import sys
index e4a1ef8b7da38f8d66c5d881beb8c9aff029073e..cf79bc6ba913bca3c68ddcd9f23c1ba04c8a39f0 100644 (file)
@@ -11,8 +11,8 @@
     </template>
     <template name="pyseq_to_cpplist_convertion">
         Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0));
-        for (int i = 0, size = PySequence_Fast_GET_SIZE(seq.object()); i &lt; size; ++i) {
-            PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i);
+        for (Py_ssize_t i = 0, size = PySequence_Size(seq.object()); i &lt; size; ++i) {
+            Shiboken::AutoDecRef pyItem(PySequence_GetItem(seq.object(), i));
             %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem);
             %out.push_back(cppItem);
         }
@@ -41,6 +41,7 @@
     <value-type name="StdOptionalTestBench"/>
 
     <system-include file-name="memory"/>
+    <system-include file-name="optional"/>
 
     <namespace-type name="std">
         <include file-name="memory" location="global"/>
@@ -50,7 +51,7 @@
                             value-check-method="operator bool"
                             ref-count-method="use_count"
                             reset-method="reset"
-                            instantiations="Integer,int=StdIntPtr,std::string">
+                            instantiations="Integer,int=StdIntPtr,double=::StdDoublePtr,std::string">
             <include file-name="memory" location="global"/>
         </smart-pointer-type>
 
index e1d078894774acb7d2f896257af30f1fbcfaf18a..c2f76167d8a3ab2c668b3b3c4e20c577b7f572bb 100644 (file)
@@ -62,4 +62,3 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/dummygentest-project.txt.in"
 declare_test(dummygentest)
 
 add_dependencies(dummygenerator generatorrunner)
-
index 8a5079820b0c4e82f81362fbf5e5c7d7960a793f..7177bc75ced0358d47a7bee814943be71008dbf0 100644 (file)
@@ -42,4 +42,3 @@ DummyGenerator::doSetup(const QMap<QString, QString>& args)
     }
     return true;
 }
-
index 0a076d8bd2c1aee3623b1d1ad442bcff1d592d68..174f82f78cf037f9c140e1dd17672224abfa3bf5 100644 (file)
@@ -17,4 +17,3 @@ api-version = 1.2.3
 debug = sparse
 
 no-suppress-warnings
-
index d439e3d8c6b03d61dad55e14e47c228545731b40..f2f6bd3e3ffa93782e3c9abecea031c9cb98f225 100644 (file)
@@ -111,4 +111,3 @@ void DummyGenTest::testProjectFileArgumentsReading()
 QTEST_APPLESS_MAIN(DummyGenTest)
 
 #include "dummygentest.moc"
-
index 1b1143b0a609357b6d03b4173ebe7ca02df6b807..ab5a69fe188bf2482d49e6e0b38eab9413bd3416 100644 (file)
@@ -28,4 +28,3 @@ private slots:
 };
 
 #endif
-
index 9da17dcd3ac343203aa50d2225516d2b2990f1ad..002d846a2f0640e3df68d5532559215a2ec8f97d 100644 (file)
@@ -12,4 +12,3 @@
 #endif
 
 #endif // DUMMYGENTESTCONFIG_H
-
index ba4440b2f11e91539c665b06f70f31f17ef4ff8e..325936d56777fc4dfe8cafaf60051aab92dfe43b 100644 (file)
@@ -11,4 +11,3 @@ int main(int argc, char *argv[])
         args.append(argv[i]);
     return QProcess::execute("generatorrunner", args);
 }
-
index 62614c4389a59ab139fb16ebbd9b36d8189137f4..8608eabdfd918f7bd318852de64b8398f06b20b6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 testing/__init__.py
index eacb51cbb7609b065dace787268a3f7c2a86189b..68a00e8f3e511136f751791586e537d052ce47e6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 testing/blacklist.py
@@ -15,7 +16,7 @@ from .buildlog import builds
 from .helper import decorate
 
 
-class BlackList(object):
+class BlackList:
     def __init__(self, blname):
         if not blname:
             f = StringIO()
index f82191f91a37d4612fa66f86d831b5a5faa59a9b..90ff436d8e4b7e2027a212c447c212fe3e90a553 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 testing/buildlog.py
@@ -22,7 +23,7 @@ LogEntry = namedtuple("LogEntry", ["log_dir", "build_dir", "build_classifiers"])
 is_ci = os.environ.get("QTEST_ENVIRONMENT", "") == "ci"
 
 
-class BuildLog(object):
+class BuildLog:
     """
     This class is a convenience wrapper around a list of log entries.
 
index 31a48f87c957a63878e4dfa19047487525e7b30e..be4ed8a07052850ade335f70875c921bfaf6a7ae 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 testrunner
@@ -133,15 +134,17 @@ def test_project(project, args, blacklist, runs):
 
 
 def main():
+    global COIN_THRESHOLD
     # create the top-level command parser
     start_time = timer()
     all_projects = "shiboken6 pyside6".split()
     tested_projects = "shiboken6 pyside6".split()
     tested_projects_quoted = " ".join("'i'" for i in tested_projects)
+    runs = COIN_TESTING
     parser = argparse.ArgumentParser(
         formatter_class=argparse.RawDescriptionHelpFormatter,
         description=dedent(
-            """\
+            f"""\
         Run the tests for some projects, default = {tested_projects_quoted}.
 
         Testing is now repeated up to {COIN_TESTING} times, and errors are
@@ -176,6 +179,8 @@ def main():
         type=int,
         help="use build number n (0-based), latest = -1 (default)",
     )
+    parser_test.add_argument("--reruns", "-r", default=COIN_TESTING, type=int,
+                             help=f"Number of re-runs (defaults to {COIN_TESTING})")
     parser_test.add_argument(
         "--projects",
         nargs="+",
@@ -208,7 +213,9 @@ def main():
         print(builds.selected.build_dir, "written to file", args.filename.name)
         sys.exit(0)
     elif args.subparser_name == "test":
-        pass  # we do it afterwards
+        runs = args.reruns
+        if runs < COIN_TESTING:
+            COIN_THRESHOLD = 1
     elif args.subparser_name == "list":
         rp = os.path.relpath
         print()
@@ -255,7 +262,6 @@ def main():
 
     q = 5 * [0]
 
-    runs = COIN_TESTING
     fail_crit = COIN_THRESHOLD
     # now loop over the projects and accumulate
     fatal = False
index d89f0d849b1a74266ca91c61a1c58aa0a93223c2..a6a2a0c3156d19f2292f6afbf327ef083a53c582 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 testing/helper.py
index a01c4d0293d287b5c270b4a8b39ab03879562244..abf00ef3ff41bbee9532a4f67e9325cc122fe3a1 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import re
@@ -120,7 +121,7 @@ def _parse_tests(test_log):
     return result
 
 
-class TestParser(object):
+class TestParser:
     def __init__(self, test_log):
         self._results = _parse_tests(test_log)
 
index b52ac49370a30709c78576ac1e3832f59c43ff5d..ad1e01d6534a3f8ed548e1f705ba6a338adf8492 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import inspect
 import os
@@ -22,7 +23,7 @@ sys.path.append(build_scripts_dir)
 from build_scripts.utils import detect_clang
 
 
-class TestRunner(object):
+class TestRunner:
     def __init__(self, log_entry, project, index):
         self.log_entry = log_entry
         built_path = log_entry.build_dir
index b36ee55a46e383b934816e0c24954b829363755f..0f6fb04ffd87d2eeeef24a1aeb409f782445975a 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 This script is used by Coin (coin_test_instructions.py specifically) to
@@ -230,8 +231,10 @@ def _run_deploy_test(example, tmpdirname):
 
     suffix = "exe" if sys.platform == "win32" else "bin"
 
+    # fetch app->title from the config
+    app_title = parser.get("app", "title")
     if sys.platform != "darwin":
-        binary = f"{tmpdirname}/{main_file.stem}.{suffix}"
+        binary = f"{tmpdirname}/{app_title}.{suffix}"
     else:
         binary = f"{tmpdirname}/pyside_app_demo.app/Contents/MacOS/{main_file.stem}"
 
@@ -347,26 +350,25 @@ def try_build_examples():
     generate_build_cmake()
     run_ninja()
 
-    if sys.version_info[:2] >= (3, 7):
-        log.info("Checking Python Interface Files in Python 3 with all features selected")
-        with tempfile.TemporaryDirectory() as tmpdirname:
-            src_path = Path(tmpdirname) / "pyi_test"
-            pyi_script_dir = Path(setup_script_dir) / "sources" / "pyside6" / "PySide6" / "support"
-            execute_script(
-                pyi_script_dir / "generate_pyi.py",
-                "all",
-                "--outpath",
-                src_path,
-                "--feature",
-                "snake_case",
-                "true_property",
-            )
-            from PySide6 import __all__ as modules
-
-            for modname in modules:
-                # PYSIDE-1735: pyi files are no longer compatible with Python.
-                # XXX Maybe add a test with Mypy here?
-                pass  # execute_script(src_path / f"{modname}.pyi")
+    log.info("Checking Python Interface Files in Python 3 with all features selected")
+    with tempfile.TemporaryDirectory() as tmpdirname:
+        src_path = Path(tmpdirname) / "pyi_test"
+        pyi_script_dir = Path(setup_script_dir) / "sources" / "pyside6" / "PySide6" / "support"
+        execute_script(
+            pyi_script_dir / "generate_pyi.py",
+            "all",
+            "--outpath",
+            src_path,
+            "--feature",
+            "snake_case",
+            "true_property",
+        )
+        from PySide6 import __all__ as modules
+
+        for modname in modules:
+            # PYSIDE-1735: pyi files are no longer compatible with Python.
+            # XXX Maybe add a test with Mypy here?
+            pass  # execute_script(src_path / f"{modname}.pyi")
 
 
 def run_wheel_tests(install_wheels, wheels_dir_name):
index 3c2ec428aa48ce8cecc41b05e5d8085e6a90b5a0..3e3355e0593664175567625a6538345590f1cc0f 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 testrunner.py
diff --git a/tools/broken_links.py b/tools/broken_links.py
new file mode 100644 (file)
index 0000000..b398155
--- /dev/null
@@ -0,0 +1,52 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+# broken_links.py
+# This script is used to find the broken links in the generated sphinx documentation.
+
+# Pre-requisites before running this script:
+#
+# 1. Generate the HTML documentation as given in
+#    https://doc.qt.io/qtforpython-6/gettingstarted/index.html#building-the-documentation
+#    . This will generate a folder inside 'pyside-setup' called 'html'.
+# 2. Run sphinx-build with linkcheck builder to generate an output.json file inside 'html' folder.
+#    The command to run is:
+#    sphinx-build -b linkcheck -j auto -n -c ./html/pyside6/base <folder_of_rst> ./html
+#
+# The final output will be <output_folder>/broken_links.json
+
+import json
+import argparse
+from pathlib import Path
+
+
+def find_broken_links(file_path):
+    with open(file_path, 'r') as file:
+        lines = file.readlines()
+
+    broken_links = []
+    for line in lines:
+        entry = json.loads(line)
+        if entry.get("status") == "broken":
+            broken_links.append(entry)
+
+    return broken_links
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Find broken links in a JSON file.")
+    parser.add_argument('--input-file', type=str, required=True, help='Path to the input JSON file')
+    parser.add_argument('--output-folder', type=str, default=Path(__file__).resolve().parents[2],
+                        help='Path to the output folder')
+    args = parser.parse_args()
+
+    broken_links = find_broken_links(args.input_file)
+
+    output_file_path = Path(args.output_folder) / 'broken_links.json'
+
+    if broken_links:
+        with open(output_file_path, 'w') as outfile:
+            json.dump(broken_links, outfile, indent=4)
+        print(f"Broken links written to {output_file_path}")
+    else:
+        print("No broken links found.")
index 9a53beade62288954bef8b637a27185739239f73..4607116ad621269c47a83dbe2fd846bbc849a8e7 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 #!/usr/bin/env python
 #
@@ -352,4 +353,3 @@ for dependency in toplevel_image.all_dependencies():
 
     dependency.dump()
     print("\n")
-
index 6c24f417fa686c1ea7eab091300681eea447d3bc..a0d49d83c097b503e0412e9294eb1568b95d86c0 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import re
 import os
@@ -8,7 +9,6 @@ import textwrap
 from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
 from pathlib import Path
 from subprocess import PIPE, Popen, check_output
-from typing import Dict, List, Tuple
 
 content_header = """Qt for Python @VERSION is a @TYPE release.
 
@@ -183,7 +183,7 @@ def get_commit_content(sha: str) -> str:
     return out.decode("utf-8")
 
 
-def git_get_sha1s(versions: List[str], pattern: str):
+def git_get_sha1s(versions: list[str], pattern: str):
     """Return a list of SHA1s matching a pattern"""
     command = "git rev-list --reverse --grep '^{}'".format(pattern)
     command += " {}..{}".format(versions[0], versions[1])
@@ -213,7 +213,7 @@ def git_get_sha1s(versions: List[str], pattern: str):
     return [s.decode("utf-8") for s in out_sha1.splitlines() if s not in pick_to_sha1]
 
 
-def git_command(versions: List[str], pattern: str):
+def git_command(versions: list[str], pattern: str):
     task_number_re = re.compile(r'^.*-(\d+)\s*$')
     for sha in git_get_sha1s(versions, pattern):
         content = get_commit_content(sha).splitlines()
@@ -232,7 +232,7 @@ def git_command(versions: List[str], pattern: str):
         task_number_match = task_number_re.match(task)
         if task_number_match:
             task_number = int(task_number_match.group(1))
-        entry = {"title": title, "task": task, "task-number": task_number}
+        entry = {"title": title, "task": task, "task-number": str(task_number)}
         if "shiboken" in title:
             if sha not in shiboken6_commits:
                 shiboken6_commits[sha] = entry
@@ -241,15 +241,15 @@ def git_command(versions: List[str], pattern: str):
                 pyside6_commits[sha] = entry
 
 
-def create_fixes_log(versions: List[str]) -> None:
+def create_fixes_log(versions: list[str]) -> None:
     git_command(versions, "Fixes: ")
 
 
-def create_task_log(versions: List[str]) -> None:
+def create_task_log(versions: list[str]) -> None:
     git_command(versions, "Task-number: ")
 
 
-def extract_change_log(commit_message: List[str]) -> Tuple[str, int, str]:
+def extract_change_log(commit_message: list[str]) -> tuple[str, int, str]:
     """Extract a tuple of (component, task-number, change log paragraph)
        from a commit message of the form [ChangeLog][shiboken6] description..."""
     result = ''
@@ -284,7 +284,7 @@ def extract_change_log(commit_message: List[str]) -> Tuple[str, int, str]:
     return (component, task_nr_int, format_text(result))
 
 
-def create_change_log(versions: List[str]) -> None:
+def create_change_log(versions: list[str]) -> None:
     for sha in git_get_sha1s(versions, r"\[ChangeLog\]"):
         change_log = extract_change_log(get_commit_content(sha).splitlines())
         component, task_nr, text = change_log
@@ -294,7 +294,7 @@ def create_change_log(versions: List[str]) -> None:
             pyside6_changelogs.append((task_nr, text))
 
 
-def format_commit_msg(entry: Dict[str, str]) -> str:
+def format_commit_msg(entry: dict[str, str]) -> str:
     task = entry["task"].replace("Fixes: ", "").replace("Task-number: ", "")
     title = entry["title"]
     if title.startswith("shiboken6: "):
@@ -304,27 +304,27 @@ def format_commit_msg(entry: Dict[str, str]) -> str:
     return format_text(f"[{task}] {title}")
 
 
-def gen_list(d: Dict[str, Dict[str, str]]) -> str:
+def gen_list(d: dict[str, dict[str, str]]) -> str:
     return "\n".join(format_commit_msg(v)
                      for _, v in d.items())
 
 
-def sort_dict(d: Dict[str, Dict[str, str]]) -> Dict[str, Dict[str, str]]:
-    return dict(sorted(d.items(), key=lambda kv: kv[1]['task-number']))
+def sort_dict(d: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]:
+    return dict(sorted(d.items(), key=lambda kv: int(kv[1]['task-number'])))
 
 
-def sort_changelog(c: List[Tuple[int, str]]) -> List[Tuple[int, str]]:
+def sort_changelog(c: list[tuple[int, str]]) -> list[tuple[int, str]]:
     return sorted(c, key=lambda task_text_tuple: task_text_tuple[0])
 
 
 if __name__ == "__main__":
 
     args = parse_options()
-    pyside6_commits: Dict[str, Dict[str, str]] = {}
-    shiboken6_commits: Dict[str, Dict[str, str]] = {}
+    pyside6_commits: dict[str, dict[str, str]] = {}
+    shiboken6_commits: dict[str, dict[str, str]] = {}
     # Changelogs are tuples of task number/formatted text
-    pyside6_changelogs: List[Tuple[int, str]] = []
-    shiboken6_changelogs: List[Tuple[int, str]] = []
+    pyside6_changelogs: list[tuple[int, str]] = []
+    shiboken6_changelogs: list[tuple[int, str]] = []
 
     exclude_pick_to = args.exclude
 
index 039fa9431cf3a0ad6ebfe983c0e41b3d9f907a02..45dd874f5a7c43e95a3979ad3b57ea5a818da608 100644 (file)
@@ -1,8 +1,10 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import logging
 import shutil
+import re
 import os
 import stat
 import sys
@@ -10,16 +12,16 @@ import subprocess
 
 from urllib import request
 from pathlib import Path
-from typing import List
 from packaging import version
 from tqdm import tqdm
 
 # the tag number does not matter much since we update the sdk later
 DEFAULT_SDK_TAG = 6514223
 ANDROID_NDK_VERSION = "26b"
+ANDROID_NDK_VERSION_NUMBER_SUFFIX = "10909125"
 
 
-def run_command(command: List[str], cwd: str = None, ignore_fail: bool = False,
+def run_command(command: list[str], cwd: str | None = None, ignore_fail: bool = False,
                 dry_run: bool = False, accept_prompts: bool = False, show_stdout: bool = False,
                 capture_stdout: bool = False):
 
@@ -83,9 +85,9 @@ class SdkManager:
                     accept_prompts=accept_license, show_stdout=show_stdout)
 
 
-def _unpack(zip_file: Path, destination: Path):
+def extract_zip(file: Path, destination: Path):
     """
-    Unpacks the zip_file into destination preserving all permissions
+    Unpacks the zip file into destination preserving all permissions
 
     TODO: Try to use zipfile module. Currently we cannot use zipfile module here because
     extractAll() does not preserve permissions.
@@ -97,10 +99,29 @@ def _unpack(zip_file: Path, destination: Path):
         raise RuntimeError("Unable to find program unzip. Use `sudo apt-get install unzip`"
                            "to install it")
 
-    command = [unzip, zip_file, "-d", destination]
+    command = [unzip, str(file), "-d", str(destination)]
     run_command(command=command, show_stdout=True)
 
 
+def extract_dmg(file: Path, destination: Path):
+    output = run_command(['hdiutil', 'attach', '-nobrowse', '-readonly', str(file)],
+                         show_stdout=True, capture_stdout=True)
+
+    # find the mounted volume
+    result = re.search(r'/Volumes/(.*)', output)
+    if not result:
+        raise RuntimeError(f"Unable to find mounted volume for file {file}")
+    mounted_vol_name = result.group(1)
+    if not mounted_vol_name:
+        raise RuntimeError(f"Unable to find mounted volume for file {file}")
+
+    # copy files
+    shutil.copytree(f'/Volumes/{mounted_vol_name}/', destination, dirs_exist_ok=True)
+
+    # Detach mounted volume
+    run_command(['hdiutil', 'detach', f'/Volumes/{mounted_vol_name}'])
+
+
 def _download(url: str, destination: Path):
     """
     Download url to destination
@@ -110,7 +131,6 @@ def _download(url: str, destination: Path):
     with DownloadProgressBar(unit='B', unit_scale=True, miniters=1, desc=url.split('/')[-1]) as t:
         download_path, headers = request.urlretrieve(url=url, filename=destination,
                                                      reporthook=t.update_to)
-    assert headers["Content-Type"] == "application/zip"
     assert Path(download_path).resolve() == destination
 
 
@@ -119,22 +139,45 @@ def download_android_ndk(ndk_path: Path):
     Downloads the given ndk_version into ndk_path
     """
     ndk_path = ndk_path / "android-ndk"
-    ndk_zip_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"
-    ndk_version_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}"
+    ndk_extension = "dmg" if sys.platform == "darwin" else "zip"
+    ndk_zip_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"
+    ndk_version_path = ""
+    if sys.platform == "linux":
+        ndk_version_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}"
+    elif sys.platform == "darwin":
+        ndk_version_path = (ndk_path
+                            / f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app/Contents/NDK")
+    else:
+        raise RuntimeError(f"Unsupported platform {sys.platform}")
 
     if ndk_version_path.exists():
         print(f"NDK path found in {str(ndk_version_path)}")
     else:
-        ndk_path.mkdir(parents=True, exist_ok=True)
-        url = (f"https://dl.google.com/android/repository"
-               f"/android-ndk-r{ANDROID_NDK_VERSION}-linux.zip")
-
-        print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
-        _download(url=url, destination=ndk_zip_path)
-
-        print("Unpacking Android Ndk")
-        _unpack(zip_file=(ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"),
-                destination=ndk_path)
+        try:
+            ndk_path.mkdir(parents=True, exist_ok=True)
+            url = (f"https://dl.google.com/android/repository"
+                   f"/android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}")
+
+            print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
+            _download(url=url, destination=ndk_zip_path)
+
+            print("Unpacking Android Ndk")
+            if sys.platform == "darwin":
+                extract_dmg(file=(ndk_path
+                            / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+                            destination=ndk_path)
+                ndk_version_path = (ndk_version_path
+                                    / (f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app"
+                                       "/Contents/NDK"))
+            else:
+                extract_zip(file=(ndk_path
+                            / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+                            destination=ndk_path)
+        except Exception as e:
+            print(f"Error occurred while downloading and unpacking Android NDK: {e}")
+            if ndk_path.exists():
+                shutil.rmtree(ndk_path)
+            sys.exit(1)
 
     return ndk_version_path
 
@@ -143,23 +186,31 @@ def download_android_commandlinetools(android_sdk_dir: Path):
     """
     Downloads Android commandline tools into cltools_path.
     """
+    sdk_platform = sys.platform if sys.platform != "darwin" else "mac"
     android_sdk_dir = android_sdk_dir / "android-sdk"
     url = ("https://dl.google.com/android/repository/"
-           f"commandlinetools-linux-{DEFAULT_SDK_TAG}_latest.zip")
-    cltools_zip_path = android_sdk_dir / f"commandlinetools-linux-{DEFAULT_SDK_TAG}_latest.zip"
+           f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
+    cltools_zip_path = (android_sdk_dir
+                        / f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
     cltools_path = android_sdk_dir / "tools"
 
     if cltools_path.exists():
         print(f"Command-line tools found in {str(cltools_path)}")
     else:
-        android_sdk_dir.mkdir(parents=True, exist_ok=True)
+        try:
+            android_sdk_dir.mkdir(parents=True, exist_ok=True)
 
-        print("Download Android Command Line Tools: "
-              f"commandlinetools-linux-{DEFAULT_SDK_TAG}_latest.zip")
-        _download(url=url, destination=cltools_zip_path)
+            print("Download Android Command Line Tools: "
+                  f"commandlinetools-{sys.platform}-{DEFAULT_SDK_TAG}_latest.zip")
+            _download(url=url, destination=cltools_zip_path)
 
-        print("Unpacking Android Command Line Tools")
-        _unpack(zip_file=cltools_zip_path, destination=android_sdk_dir)
+            print("Unpacking Android Command Line Tools")
+            extract_zip(file=cltools_zip_path, destination=android_sdk_dir)
+        except Exception as e:
+            print(f"Error occurred while downloading and unpacking Android Command Line Tools: {e}")
+            if android_sdk_dir.exists():
+                shutil.rmtree(android_sdk_dir)
+            sys.exit(1)
 
     return android_sdk_dir
 
@@ -204,6 +255,10 @@ def find_latest_buildtools_version(sdk_manager: SdkManager):
     if not available_build_tools_v:
         raise RuntimeError('Unable to find any build tools available for download')
 
+    # find the latest build tools version that is not a release candidate
+    # release candidates end has rc in the version number
+    available_build_tools_v = [v for v in available_build_tools_v if "rc" not in str(v)]
+
     return max(available_build_tools_v)
 
 
index bda438ccade38beb47b47738ce283ea6b2923cac..2884793caced8948251acf8a92852f0c8c458433 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import sys
 import logging
@@ -10,7 +11,7 @@ import shutil
 from dataclasses import dataclass
 
 from pathlib import Path
-from git import Repo, RemoteProgress
+from git import Repo, RemoteProgress, GitCommandError
 from tqdm import tqdm
 from jinja2 import Environment, FileSystemLoader
 
@@ -18,8 +19,13 @@ from android_utilities import (run_command, download_android_commandlinetools,
                                download_android_ndk, install_android_packages)
 
 # Note: Does not work with PyEnv. Your Host Python should contain openssl.
+# also update the version in ShibokenHelpers.cmake if Python version changes.
 PYTHON_VERSION = "3.11"
 
+# minimum Android API version support. This is set according to Qt's requiremnts and needs to
+# be updated if Qt's minimum API level is updated.
+MIN_ANDROID_API_LEVEL = "28"
+
 SKIP_UPDATE_HELP = ("skip the updation of SDK packages build-tools, platform-tools to"
                     " latest version")
 
@@ -43,6 +49,11 @@ Options:
 If --clean-cache is used and no explicit value is suppied, then `all` is used as default.
 ''')
 
+COIN_RUN_HELP = ('''
+When run by Qt's continuos integration system COIN. This option is irrelevant to user building
+their own wheels.
+''')
+
 
 @dataclass
 class PlatformData:
@@ -61,6 +72,13 @@ def occp_exists():
     return "-occp" in sys.argv or "--only-cross-compile-python" in sys.argv
 
 
+def download_only_exists():
+    '''
+    check if '--download-only' exists in command line arguments
+    '''
+    return "--download-only" in sys.argv
+
+
 class CloneProgress(RemoteProgress):
     def __init__(self):
         super().__init__()
@@ -86,12 +104,17 @@ if __name__ == "__main__":
 
     parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
                         dest="loglevel", const=logging.INFO)
-    parser.add_argument("--api-level", type=str, default="33", help="Android API level to use")
-    parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r25c)")
+    parser.add_argument("--api-level", type=str, default="34",
+                        help="Minimum Android API level to use")
+    parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r26b)")
     # sdk path is needed to compile all the Qt Java Acitivity files into Qt6AndroidBindings.jar
     parser.add_argument("--sdk-path", type=str, help="Path to Android SDK")
-    parser.add_argument("--qt-install-path", type=str, required=not occp_exists(),
-                        help="Qt installation path eg: /home/Qt/6.5.0")
+    parser.add_argument(
+        "--qt-install-path",
+        type=str,
+        required=not (occp_exists() or download_only_exists()),
+        help="Qt installation path eg: /home/Qt/6.8.0"
+    )
 
     parser.add_argument("-occp", "--only-cross-compile-python", action="store_true",
                         help="Only cross compiles Python for the specified Android platform")
@@ -108,6 +131,12 @@ if __name__ == "__main__":
                         choices=["all", "python", "ndk", "sdk", "toolchain"],
                         help=CLEAN_CACHE_HELP)
 
+    parser.add_argument("--coin", action="store_true",
+                        help=COIN_RUN_HELP)
+
+    parser.add_argument("--download-only", action="store_true",
+                        help="Only download Android NDK and SDK")
+
     args = parser.parse_args()
 
     logging.basicConfig(level=args.loglevel)
@@ -125,6 +154,8 @@ if __name__ == "__main__":
     skip_update = args.skip_update
     auto_accept_license = args.auto_accept_license
     clean_cache = args.clean_cache
+    coin = args.coin
+    download_only = args.download_only
 
     # auto download Android NDK and SDK
     pyside6_deploy_cache = Path.home() / ".pyside6_android_deploy"
@@ -162,8 +193,13 @@ if __name__ == "__main__":
         # download and unzip command-line tools
         sdk_path = download_android_commandlinetools(pyside6_deploy_cache)
         # install and update required android packages
-        install_android_packages(android_sdk_dir=sdk_path, android_api=api_level, dry_run=dry_run,
-                                 accept_license=auto_accept_license, skip_update=skip_update)
+        install_android_packages(android_sdk_dir=sdk_path, android_api=api_level,
+                                 dry_run=dry_run, accept_license=auto_accept_license,
+                                 skip_update=skip_update)
+
+    if download_only:
+        print(f"Android NDK and SDK downloaded successfully into {pyside6_deploy_cache}")
+        sys.exit(0)
 
     templates_path = Path(__file__).parent / "templates"
 
@@ -184,8 +220,8 @@ if __name__ == "__main__":
             platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
 
         # python path is valid, if Python for android installation exists in python_path
-        python_path = (pyside6_deploy_cache / f"Python-{platform_data.plat_name}-linux-android"
-                       / "_install")
+        python_path = (pyside6_deploy_cache
+                       / f"Python-{platform_data.plat_name}-linux-android" / "_install")
         valid_python_path = python_path.exists()
         if Path(python_path).exists():
             expected_dirs = ["lib", "include"]
@@ -206,14 +242,25 @@ if __name__ == "__main__":
 
             if not cpython_dir.exists():
                 logging.info(f"cloning cpython {PYTHON_VERSION}")
-                Repo.clone_from(
-                    "https://github.com/python/cpython.git",
-                    cpython_dir,
-                    progress=CloneProgress(),
-                    branch=PYTHON_VERSION,
-                )
+                try:
+                    Repo.clone_from(
+                        "https://github.com/python/cpython.git",
+                        cpython_dir,
+                        progress=CloneProgress(),
+                        branch=PYTHON_VERSION,
+                    )
+                except GitCommandError as e:
+                    # Print detailed error information
+                    print(f"Error cloning repository: {e}")
+                    print(f"Command: {e.command}")
+                    print(f"Status: {e.status}")
+                    print(f"Stderr: {e.stderr}")
 
             if not python_ccompile_script.exists():
+                host_system_config_name = run_command("./config.guess", cwd=cpython_dir,
+                                                      dry_run=dry_run, show_stdout=True,
+                                                      capture_stdout=True).strip()
+
                 # use jinja2 to create cross_compile.sh script
                 template = environment.get_template("cross_compile.tmpl.sh")
                 content = template.render(
@@ -221,7 +268,10 @@ if __name__ == "__main__":
                     ndk_path=ndk_path,
                     api_level=platform_data.api_level,
                     android_py_install_path_prefix=pyside6_deploy_cache,
-                    host_python_path=sys.executable
+                    host_python_path=sys.executable,
+                    python_version=PYTHON_VERSION,
+                    host_system_name=host_system_config_name,
+                    host_platform_name=sys.platform
                 )
 
                 logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
@@ -240,13 +290,6 @@ if __name__ == "__main__":
             run_command([f"./{python_ccompile_script.name}"], cwd=cpython_dir, dry_run=dry_run,
                         show_stdout=True)
 
-            # run patchelf to change the SONAME of libpython from libpython3.x.so.1.0 to
-            # libpython3.x.so, to match with python_for_android's Python library. Otherwise,
-            # the Qfp binaries won't be able to link to Python
-            run_command(["patchelf", "--set-soname", f"libpython{PYTHON_VERSION}.so",
-                        f"libpython{PYTHON_VERSION}.so.1.0"], cwd=Path(python_path) / "lib",
-                        dry_run=dry_run)
-
             logging.info(
                 f"Cross compile Python for Android platform {platform_data.plat_name}. "
                 f"Final installation in {python_path}"
@@ -276,7 +319,8 @@ if __name__ == "__main__":
                 gcc_march=platform_data.gcc_march,
                 plat_bits=platform_data.plat_bits,
                 python_version=PYTHON_VERSION,
-                target_python_path=python_path
+                target_python_path=python_path,
+                min_android_api=MIN_ANDROID_API_LEVEL
             )
 
             logging.info(f"Writing Qt for Python toolchain file into {qfp_toolchain}")
@@ -286,15 +330,28 @@ if __name__ == "__main__":
             # give run permission to cross compile script
             qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
 
+        if sys.platform == "linux":
+            host_qt_install_suffix = "gcc_64"
+        elif sys.platform == "darwin":
+            host_qt_install_suffix = "macos"
+        else:
+            raise RuntimeError("Qt for Python cross compilation not supported on this platform")
+
+        if coin:
+            target_path = str(Path(qt_install_path) / "target")
+            qt_host_install_path = qt_install_path
+        else:
+            target_path = str(Path(qt_install_path) / f"android_{platform_data.qt_plat_name}")
+            qt_host_install_path = str(Path(qt_install_path) / host_qt_install_suffix)
+
         # run the cross compile script
         logging.info(f"Running Qt for Python cross-compile for platform {platform_data.plat_name}")
         qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
                             "--standalone",
                             f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
-                            f"--qt-host-path={qt_install_path}/gcc_64",
+                            f"--qt-host-path={qt_host_install_path}",
                             f"--plat-name=android_{platform_data.plat_name}",
                             f"--python-target-path={python_path}",
-                            (f"--qt-target-path={qt_install_path}/"
-                                f"android_{platform_data.qt_plat_name}"),
+                            f"--qt-target-path={target_path}",
                             "--no-qt-tools"]
         run_command(qfp_ccompile_cmd, cwd=pyside_setup_dir, dry_run=dry_run, show_stdout=True)
index 62e8ee3b0376aa09d394089b4aad8d49365913e3..6d272b70381c96bf1188ab8da8d812a0110a313d 100644 (file)
@@ -1,3 +1,6 @@
 gitpython
+# Fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' caused when jinja2 is
+# installed
+markupsafe==2.0.1
 Jinja2
 tqdm
index a68907591a4360eb8ded87202b976c994ee0fc45..784e822ca83a5c4988452069f1b49e63e8bc6799 100644 (file)
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 set -x -e
 export HOST_ARCH={{ plat_name }}-linux-android
-export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/linux-x86_64/bin
+export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/{{ host_platform_name }}-x86_64/bin
 export TOOL_PREFIX=$TOOLCHAIN/$HOST_ARCH
 export PLATFORM_API={{ api_level }}
 {% if plat_name == "armv7a" -%}
@@ -20,10 +20,10 @@ export RANLIB=$TOOLCHAIN/llvm-ranlib
 export LD=$TOOLCHAIN/ld
 export READELF=$TOOLCHAIN/llvm-readelf
 export CFLAGS='-fPIC -DANDROID'
-./configure --host=$HOST_ARCH --target=$HOST_ARCH --build=x86_64-pc-linux-gnu \
+./configure --host=$HOST_ARCH --target=$HOST_ARCH --build={{ host_system_name }} \
 --with-build-python={{ host_python_path }}  --enable-shared \
 --enable-ipv6 ac_cv_file__dev_ptmx=yes ac_cv_file__dev_ptc=no --without-ensurepip \
 ac_cv_little_endian_double=yes
-make BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- CROSS_COMPILE_TARGET=yes
-make install BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- \
-CROSS_COMPILE_TARGET=yes prefix={{ android_py_install_path_prefix }}/Python-$HOST_ARCH/_install
+make BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- CROSS_COMPILE_TARGET=yes \
+INSTSONAME=libpython{{ python_version }}.so
+make install prefix={{ android_py_install_path_prefix }}/Python-$HOST_ARCH/_install
index 3c9752f4358bc919a13b8d6ded1d2eba926c96c0..c1cf6686f2e5197a62af8bded2c9a761416c967c 100644 (file)
@@ -16,7 +16,7 @@ set(CMAKE_ANDROID_ARCH_ABI {{ android_abi }})
 set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)
 set(CMAKE_ANDROID_STL_TYPE c++_shared)
 if(NOT DEFINED ANDROID_PLATFORM AND NOT DEFINED ANDROID_NATIVE_API_LEVEL)
-    set(ANDROID_PLATFORM "android-{{ api_level }}" CACHE STRING "")
+    set(ANDROID_PLATFORM "android-{{ min_android_api }}" CACHE STRING "")
 endif()
 set(ANDROID_SDK_ROOT {{ sdk_path }})
 {% if plat_name == "armv7a" -%}
index ec777388b8a5ba43b663f5d17ddcd437f68a8ac9..cf9994b8f295addfcfdf3cf06224b9ff1b0a57df 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 import re
 import sys
index 8322828957f1a907e2986fb6a8425443dae08d3c..70a3d402cb57f659db0fe038883c5892c988eee4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import argparse
 import ctypes
index d46f4db02827c25a7d7863cc62da46e0e2218da6..3977bd69ee038d9423ad428d6bdef4290e155650 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import subprocess
index 6898e9317ff09b688c94ac6cc27175cdbdd28b32..baf659a6b2a3e3053bd5c95bd0ba71037a02815b 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """Helper functions for formatting information on QMetaObject"""
 
index b5aa632c046c88873aa392025d29d5468a28f09f..1b66b566805013787711faa88c10c04a65868068 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 
 """
@@ -23,6 +24,7 @@ from dataclasses import dataclass
 from enum import IntEnum, Enum
 from pathlib import Path
 from textwrap import dedent
+from collections import defaultdict
 
 
 class Format(Enum):
@@ -66,6 +68,8 @@ suffixes = {
 
 
 BASE_CONTENT = """\
+.. _pyside6_examples:
+
 Examples
 ========
 
@@ -80,6 +84,16 @@ Examples
 """
 
 
+def tutorial_headline(path: str):
+    if "tutorials/extending-qml/chapter" in path:
+        return "Tutorial: Writing QML Extensions with Python"
+    if "tutorials/extending-qml-advanced/advanced" in path:
+        return "Tutorial: Writing advanced QML Extensions with Python"
+    if "tutorials/finance_manager" in path:
+        return "Tutorial: Finance Manager - Integrating PySide6 with SQLAlchemy and FastAPI"
+    return ""
+
+
 def ind(x):
     return " " * 4 * x
 
@@ -203,6 +217,7 @@ class ExampleData:
     has_doc: bool
     img_doc: Path
     headline: str
+    tutorial: str
 
 
 def get_module_gallery(examples):
@@ -212,15 +227,20 @@ def get_module_gallery(examples):
     """
 
     gallery = (
-        ".. grid:: 1 4 4 4\n"
-        f"{ind(1)}:gutter: 2\n\n"
+        f"{ind(1)}.. grid:: 1 3 3 3\n"
+        f"{ind(2)}:gutter: 3\n\n"
     )
 
     # Iteration per rows
     for i in range(math.ceil(len(examples))):
         e = examples[i]
         suffix = SUFFIXES[e.file_format]
-        url = e.doc_file.replace(f".{suffix}", ".html")
+        # doc_file with suffix removed, to be used as a sphinx reference
+        doc_file_name = e.doc_file.replace(f".{suffix}", "")
+        # lower case sphinx reference
+        # this seems to be a bug or a requirement from sphinx
+        doc_file_name = doc_file_name.lower()
+
         name = e.example
         underline = e.module
 
@@ -237,15 +257,94 @@ def get_module_gallery(examples):
         elif name.startswith("advanced"):
             name = name.replace("advanced", "a")
 
-        desc = e.headline
-        if not desc:
-            desc = f"found in the ``{underline}`` directory."
+        # Handling description from original file
+        desc = ""
+        original_dir = Path(e.abs_path) / "doc"
+
+        if e.has_doc:
+            # cannot use e.doc_file because that is the target file name
+            # so finding the original file by the name
+            original_file = (next(original_dir.glob("*.rst"), None)
+                             or next(original_dir.glob("*.md"), None))
+            if not original_file:
+                # ideally won't reach here because has_doc is True
+                print(f"example_gallery: No .rst or .md file found in {original_dir}")
+                continue
+
+            with original_file.open("r") as f:
+                # Read the first line
+                first_line = f.readline().strip()
+
+                # Check if the first line is a reference (starts with '(' and ends with ')=' for MD,
+                # or starts with '.. ' and ends with '::' for RST)
+                if ((e.file_format == Format.MD and first_line.startswith('(')
+                     and first_line.endswith(')='))
+                    or (e.file_format == Format.RST and first_line.startswith('.. ')
+                        and first_line.endswith('::'))):
+                    # The first line is a reference, so read the next lines until a non-empty line
+                    # is found
+                    while True:
+                        title_line = f.readline().strip()
+                        if title_line:
+                            break
+                else:
+                    # The first line is the title
+                    title_line = first_line
+
+                # The next line handling depends on the file format
+                line = f.readline().strip()
+
+                if e.file_format == Format.MD:
+                    # For markdown, the second line is the empty line
+                    if line != "":
+                        # If the line is not empty, raise a runtime error
+                        raise RuntimeError(f"Unexpected line: {line} in {original_file}. "
+                                           "Needs handling.")
+                else:
+                    # For RST and other formats
+                    # The second line is the underline under the title
+                    _ = line
+                    # The next line should be empty
+                    line = f.readline().strip()
+                    if line != "":
+                        raise RuntimeError(f"Unexpected line: {line} in {original_file}. "
+                                           "Needs handling.")
+
+                # Now read until another empty line
+                lines = []
+                while True:
+                    line = f.readline().strip()
+                    if line.startswith(".. tags") or line.startswith("#"):
+                        # Skip the empty line
+                        _ = f.readline()
+                        # Read the next line
+                        line = f.readline().strip()
+
+                    if not line:
+                        break
+                    lines.append(line)
+
+                desc = " ".join(lines)
+                if len(desc) > 120:
+                    desc = desc[:120] + "..."
+        else:
+            print(f"example_gallery: No .rst or .md file found in {original_dir}")
+
+        title = e.headline
+        if not title:
+            title = f"{name} from ``{underline}``."
+
+        # Clean refs from desc
+        if ":ref:" in desc:
+            desc = desc.replace(":ref:`", "")
+        desc = desc.replace("`", "")
 
-        gallery += f"{ind(1)}.. grid-item-card:: {name}\n"
-        gallery += f"{ind(2)}:class-item: cover-img\n"
-        gallery += f"{ind(2)}:link: {url}\n"
-        gallery += f"{ind(2)}:img-top: {img_name}\n\n"
-        gallery += f"{ind(2)}{desc}\n"
+        gallery += f"{ind(2)}.. grid-item-card:: {title}\n"
+        gallery += f"{ind(3)}:class-item: cover-img\n"
+        gallery += f"{ind(3)}:link: {doc_file_name}\n"
+        gallery += f"{ind(3)}:link-type: ref\n"
+        gallery += f"{ind(3)}:img-top: {img_name}\n\n"
+        gallery += f"{ind(3)}+++\n{ind(3)}{desc}\n"
 
     return f"{gallery}\n"
 
@@ -259,14 +358,14 @@ def remove_licenses(s):
     return "\n".join(new_s)
 
 
-def make_zip_archive(zip_name, src, skip_dirs=None):
+def make_zip_archive(zip_file, src, skip_dirs=None):
     src_path = Path(src).expanduser().resolve(strict=True)
     if skip_dirs is None:
         skip_dirs = []
     if not isinstance(skip_dirs, list):
         print("Error: A list needs to be passed for 'skip_dirs'")
         return
-    with zipfile.ZipFile(src_path.parents[0] / Path(zip_name), 'w', zipfile.ZIP_DEFLATED) as zf:
+    with zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED) as zf:
         for file in src_path.rglob('*'):
             skip = False
             _parts = file.relative_to(src_path).parts
@@ -291,11 +390,10 @@ def get_code_tabs(files, project_dir, file_format):
     content = "\n"
 
     # Prepare ZIP file, and copy to final destination
-    zip_name = f"{project_dir.name}.zip"
-    make_zip_archive(zip_name, project_dir, skip_dirs=["doc"])
-    zip_src = f"{project_dir}.zip"
-    zip_dst = EXAMPLES_DOC / zip_name
-    shutil.move(zip_src, zip_dst)
+    # Handle examples which only have a dummy pyproject file in the "doc" dir
+    zip_root = project_dir.parent if project_dir.name == "doc" else project_dir
+    zip_name = f"{zip_root.name}.zip"
+    make_zip_archive(EXAMPLES_DOC / zip_name, zip_root, skip_dirs=["doc"])
 
     if file_format == Format.RST:
         content += f":download:`Download this example <{zip_name}>`\n\n"
@@ -466,6 +564,10 @@ def detect_pyside_example(example_root, pyproject_file):
     p = ExampleParameters()
 
     p.example_dir = pyproject_file.parent
+    if list(p.example_dir.parent.glob("*.qmlproject")) and p.example_dir.name == "Python":
+        # Design Studio project example
+        p.example_dir = pyproject_file.parent.parent
+
     if p.example_dir.name == "doc":  # Dummy pyproject in doc dir (scriptableapplication)
         p.example_dir = p.example_dir.parent
 
@@ -529,6 +631,7 @@ def write_example(example_root, pyproject_file, pyside_example=True):
     result.abs_path = str(p.example_dir)
     result.has_doc = bool(p.src_doc_file_path)
     result.img_doc = p.src_screenshot
+    result.tutorial = tutorial_headline(result.abs_path)
 
     files = []
     try:
@@ -549,9 +652,22 @@ def write_example(example_root, pyproject_file, pyside_example=True):
     headline = ""
     if files:
         doc_file = EXAMPLES_DOC / p.target_doc_file
+        sphnx_ref_example = p.target_doc_file.replace(f'.{SUFFIXES[p.file_format]}', '')
+        # lower case sphinx reference
+        # this seems to be a bug or a requirement from sphinx
+        sphnx_ref_example = sphnx_ref_example.lower()
+        content_f = ""
+        if p.file_format == Format.RST:
+            content_f = f".. _{sphnx_ref_example}:\n\n"
+        elif p.file_format == Format.MD:
+            content_f = f"({sphnx_ref_example})=\n\n"
+        else:
+            print(f"example_gallery: Invalid file format {p.file_format}", file=sys.stderr)
+            raise ValueError
+
         with open(doc_file, "w", encoding="utf-8") as out_f:
             if p.src_doc_file_path:
-                content_f = read_rst_file(p.example_dir, files, p.src_doc_file_path)
+                content_f += read_rst_file(p.example_dir, files, p.src_doc_file_path)
                 headline = get_headline(content_f, p.file_format)
                 if not headline:
                     print(f"example_gallery: No headline found in {doc_file}",
@@ -570,7 +686,7 @@ def write_example(example_root, pyproject_file, pyside_example=True):
                         resources.append(p.src_screenshot)
                 write_resources(resources, EXAMPLES_DOC)
             else:
-                content_f = get_header_title(p.example_dir)
+                content_f += get_header_title(p.example_dir)
             content_f += get_code_tabs(files, pyproject_file.parent, p.file_format)
             out_f.write(content_f)
 
@@ -586,8 +702,13 @@ def write_example(example_root, pyproject_file, pyside_example=True):
 
 
 def example_sort_key(example: ExampleData):
-    name = example.example
-    return "AAA" + name if "gallery" in name else name
+    result = ""
+    if example.tutorial:
+        result += "AA:" + example.tutorial + ":"
+    elif "gallery" in example.example:
+        result += "AB:"
+    result += example.example
+    return result
 
 
 def sort_examples(example):
@@ -672,12 +793,32 @@ if __name__ == "__main__":
         f.write(BASE_CONTENT)
         for module_name in sorted(examples.keys(), key=module_sort_key):
             e = examples.get(module_name)
-            for i in e:
-                index_files.append(i.doc_file)
+            tutorial_examples = defaultdict(list)
+            non_tutorial_examples = []
+
+            for example in e:
+                index_files.append(example.doc_file)
+                if example.tutorial:
+                    tutorial_examples[example.tutorial].append(example)
+                else:
+                    non_tutorial_examples.append(example)
+
             title = module_title(module_name)
-            f.write(f"{title}\n")
-            f.write(f"{'*' * len(title)}\n")
-            f.write(get_module_gallery(e))
+            f.write(f".. dropdown:: {title}\n\n")
+
+            # Write tutorial examples under their tutorial names
+            for tutorial_name, tutorial_exs in tutorial_examples.items():
+                f.write(f"{ind(1)}**{tutorial_name}**\n\n")
+                f.write(get_module_gallery(tutorial_exs))
+
+            # If there are non-tutorial examples and tutorials exist
+            if tutorial_examples and non_tutorial_examples:
+                f.write(f"{ind(1)}**Other Examples**\n\n")
+                f.write(get_module_gallery(non_tutorial_examples))
+            # If no tutorials exist, list all examples
+            elif not tutorial_examples:
+                f.write(get_module_gallery(e))
+
         f.write("\n\n")
         f.write(footer_index)
         for i in index_files:
diff --git a/tools/install-p311.sh b/tools/install-p311.sh
new file mode 100755 (executable)
index 0000000..d56fd34
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/bash
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+if [ ! -d "/Users/qt/python311/bin" ]; then
+    cd /Users/qt/work
+    curl -O https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tar.xz
+    if [ $? -ne 0 ]; then
+        echo "Failed to download Python source code."
+        exit 1
+    fi
+
+    tar xJf Python-3.11.9.tar.xz
+    if [ $? -ne 0 ]; then
+        echo "Failed to extract Python source code."
+        exit 1
+    fi
+
+    cd Python-3.11.9/
+    ./configure --prefix=/Users/qt/python311 --with-openssl=/usr/local/opt/openssl --enable-optimizations
+    if [ $? -ne 0 ]; then
+        echo "Failed to configure Python."
+        exit 1
+    fi
+    make
+    if [ $? -ne 0 ]; then
+        echo "Failed to compile Python."
+        exit 1
+    fi
+    make install
+    if [ $? -ne 0 ]; then
+        echo "Failed to install Python."
+        exit 1
+    fi
+fi
index 8a21c2337a7cf56b514a7a49b0cdfe50541c728e..fb3505be33eae3ad36c187efefa622feab6d55b6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
 
 """
 leak_finder.py
@@ -79,7 +80,7 @@ except AttributeError:
     have_debug = False
 
 
-class LeakFinder(object):
+class LeakFinder:
     def __init__(self):
         self.all, self.refs = self._make_snapshot()
 
index 4b12a05fd5761c594f07b26d6bac5deb12a43d61..0f9a193848955092377ab25130f90a697dc50bf4 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import subprocess
index b6cde13ef016345bd3071900e47efc808f2d64f8..3bfcfe9c6482112cab7cb44d9d9cc6876b3ac31e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import sys
 
index ddaf206857a45f0bd07a7749f5828f3859c5b780..b6d98b5fc5ed7a77d1a65e79273cfba822480934 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 modules_to_test = {
     # 6.0
@@ -11,6 +12,7 @@ modules_to_test = {
     'QtQuickWidgets': 'qtquickwidgets-module.html',
     # Broken in 6.5.0
     #'QtQuickControls2': 'qtquickcontrols-module.html',
+    'QtQuickControls2': 'qtquickcontrols2-module.html',
     'QtSql': 'qtsql-module.html',
     'QtWidgets': 'qtwidgets-module.html',
     'QtConcurrent': 'qtconcurrent-module.html',
@@ -22,53 +24,64 @@ modules_to_test = {
     'QtSvgWidgets': 'qtsvgwidgets-module.html',
     'QtUiTools': 'qtuitools-module.html',
     'QtXml': 'qtxml-module.html',
+    'QtQuickTest': 'qtquicktest-module.html',
     'QtTest': 'qttest-module.html',
-    'Qt3DCore': 'qt3dcore-module.html',
-    'Qt3DInput': 'qt3dinput-module.html',
-    'Qt3DLogic': 'qt3dlogic-module.html',
-    'Qt3DRender': 'qt3drender-module.html',
-    'Qt3DAnimation': 'qt3danimation-module.html',
-    'Qt3DExtras': 'qt3dextras-module.html',
-    'QtNetworkAuth':  'qtnetworkauth-module.html',
+    # Deprecated in 6.8
+    #'Qt3DCore': 'qt3dcore-module.html',
+    #'Qt3DInput': 'qt3dinput-module.html',
+    #'Qt3DLogic': 'qt3dlogic-module.html',
+    #'Qt3DRender': 'qt3drender-module.html',
+    #'Qt3DAnimation': 'qt3danimation-module.html',
+    #'Qt3DExtras': 'qt3dextras-module.html',
+    'QtNetworkAuth': 'qtnetworkauth-module.html',
     'QtStateMachine': 'qtstatemachine-module.html',
-    # 'QtCoAp' -- TODO
-    # 'QtMqtt' -- TODO
-    # 'QtOpcUA' -- TODO
+    'QtCoap': 'qtcoap-module.html',
+    'QtMqtt': 'qtmqtt-module.html',
+    'QtOpcUa': 'qtopcua-module.html',
+    'QtVirtualKeyboard': 'qtvirtualkeyboard-module.html',
+    'QtWaylandCompositor': 'qtwaylandcompositor-module.html',
+    'QtWebView': 'qtwebview-module.html',
 
     # 6.1
-    'QtScxml':  'qtscxml-module.html',
-    'QtCharts':  'qtcharts-module.html',
-    'QtDataVisualization':  'qtdatavisualization-module.html',
+    'QtScxml': 'qtscxml-module.html',
 
     # 6.2
     'QtBluetooth': 'qtbluetooth-module.html',
-    'QtPositioning':  'qtpositioning-module.html',
-    'QtMultimedia':  'qtmultimedia-module.html',
-    'QtRemoteObjects':  'qtremoteobjects-module.html',
-    'QtSensors':  'qtsensors-module.html',
-    'QtSerialPort':  'qtserialport-module.html',
-    'QtWebChannel':  'qtwebchannel-module.html',
-    'QtWebEngineCore':  'qtwebenginecore-module.html',
-    'QtWebEngineQuick':  'qtwebenginequick-module.html',
-    'QtWebEngineWidgets':  'qtwebenginewidgets-module.html',
-    'QtWebSockets':  'qtwebsockets-module.html',
+    'QtPositioning': 'qtpositioning-module.html',
+    'QtMultimedia': 'qtmultimedia-module.html',
+    'QtRemoteObjects': 'qtremoteobjects-module.html',
+    'QtSensors': 'qtsensors-module.html',
+    'QtSerialPort': 'qtserialport-module.html',
+    'QtWebChannel': 'qtwebchannel-module.html',
+    'QtWebEngineCore': 'qtwebenginecore-module.html',
+    'QtWebEngineQuick': 'qtwebenginequick-module.html',
+    'QtWebEngineWidgets': 'qtwebenginewidgets-module.html',
+    'QtWebSockets': 'qtwebsockets-module.html',
     'QtHttpServer': 'qthttpserver-module.html',
 
     #  6.3
     #'QtSpeech':  'qtspeech-module.html',
-    'QtMultimediaWidgets':  'qtmultimediawidgets-module.html',
+    'QtMultimediaWidgets': 'qtmultimediawidgets-module.html',
     'QtNfc': 'qtnfc-module.html',
     'QtQuick3D': 'qtquick3d-module.html',
 
     # 6.4
-    'QtPdf':  'qtpdf-module.html',  # this include qtpdfwidgets
+    'QtPdf': 'qtpdf-module.html',  # this include qtpdfwidgets
     'QtSpatialAudio': 'qtspatialaudio-module.html',
 
     # 6.5
-    'QtSerialBus':  'qtserialbus-module.html',
+    'QtSerialBus': 'qtserialbus-module.html',
     'QtTextToSpeech': 'qttexttospeech-module.html',
-    'QtLocation':  'qtlocation-module.html',
+    'QtLocation': 'qtlocation-module.html',
+    'QtGRPC': 'qtgrpc-module.html',
+    'QtPhotobuf': 'qtprotobuf-module.html',
 
+    # 6.6
+    'QtGraphs': 'qtgraphs-module.html',
+
+    # 6.7
+    'QtCharts': 'qtcharts-module-qtcharts.html',
+    'QtDataVisualization': 'qtdatavisualization-module-qtdatavis.html',
 }
 
 types_to_ignore = {
@@ -142,3 +155,11 @@ types_to_ignore = {
     'QSqlDriverCreator',
     'QSqlDriverPlugin',
 }
+
+# Split modules on PySide
+split_modules = {
+    'QtSvg': 'QtSvgWidgets',
+    'QtPdf': 'QtPdfWidgets',
+    'QtOpenGL': 'QtOpenGLWidgets',
+    'QtGraphs': 'QtGraphsWidgets',
+}
index 4c223050dbc65a94f2b4223121ff821857f801e7..bf9975ebfc19d5f6903fd91bdaebd046dbefb9ee 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 # This script is used to generate a summary of missing types / classes
 # which are present in C++ Qt6, but are missing in PySide6.
@@ -25,12 +26,12 @@ from urllib import request
 from pathlib import Path
 
 from bs4 import BeautifulSoup
-from config import modules_to_test, types_to_ignore
+from config import modules_to_test, types_to_ignore, split_modules
 import pandas as pd
 import matplotlib.pyplot as plt
 
 qt_documentation_website_prefixes = {
-    "6.5": "https://doc.qt.io/qt-6/",
+    "6.7": "https://doc.qt.io/qt-6/",
     "dev": "https://doc-snapshots.qt.io/qt6-dev/",
 }
 
@@ -59,8 +60,8 @@ def get_parser():
     parser.add_argument(
         "--qt-version",
         "-v",
-        default="6.5",
-        choices=["6.5", "dev"],
+        default="6.7",
+        choices=["6.7", "dev"],
         type=str,
         dest="version",
         help="the Qt version to use to check for types",
@@ -156,10 +157,10 @@ if __name__ == "__main__":
         style="end",
     )
 
-    wikilog(
-        "Similar report:\n https://gist.github.com/ethanhs/6c626ca4e291f3682589699296377d3a",
-        style="text_with_link",
-    )
+    # wikilog(
+    #     "Similar report:\n https://gist.github.com/ethanhs/6c626ca4e291f3682589699296377d3a",
+    #     style="text_with_link",
+    # )
 
     python_executable = Path(sys.executable).name or ""
     command_line_arguments = " ".join(sys.argv)
@@ -187,6 +188,13 @@ if __name__ == "__main__":
             pyside_tested_module = getattr(
                 __import__(pyside_package_name, fromlist=[module_name]), module_name
             )
+            for main_module, split_module in split_modules.items():
+                # If module name matches with the split_modules
+                # Second module is also imported
+                if module_name == main_module:
+                    pyside_tested_module_split = getattr(
+                        __import__(pyside_package_name, fromlist=[split_module]), split_module
+                    )
         except Exception as e:
             e_str = str(e).replace('"', "")
             wikilog(
@@ -201,7 +209,7 @@ if __name__ == "__main__":
             pyqt_module_name = module_name
 
             pyqt_tested_module = getattr(
-                __import__(pyqt_package_name, fromlist=[pyqt_module_name]), pyqt_module_name
+                __import__(pyqt_package_name, fromlist=[module_name]), pyqt_module_name
             )
         except Exception as e:
             e_str = str(e).replace("'", "")
@@ -250,17 +258,24 @@ if __name__ == "__main__":
                 pyside_qualified_type = f"pyside_tested_module.{qt_type}"
                 eval(pyside_qualified_type)
                 is_present_in_pyside = True
-            except Exception as e:
-                print("Failed eval-in pyside qualified types")
-                print(f"{type(e).__name__}: {e}")
-                missing_type = qt_type
-                missing_pyside_types_count += 1
-                total_missing_types_count += 1
-
-                if is_present_in_pyqt:
-                    missing_type = f"{missing_type} (is present in PyQt6)"
-                    missing_types_compared_to_pyqt += 1
-                    total_missing_types_count_compared_to_pyqt += 1
+            except Exception:
+                # If an exception is thrown in the first eval
+                # Second imported module will be checked
+                try:
+                    pyside_qualified_type_split = f"pyside_tested_module_split.{qt_type}"
+                    eval(pyside_qualified_type_split)
+                    is_present_in_pyside = True
+                except Exception as e:
+                    print("Failed eval-in pyside qualified types")
+                    print(f"{type(e).__name__}: {e}")
+                    missing_type = qt_type
+                    missing_pyside_types_count += 1
+                    total_missing_types_count += 1
+
+                    if is_present_in_pyqt:
+                        missing_type = f"{missing_type} (is present in PyQt6)"
+                        missing_types_compared_to_pyqt += 1
+                        total_missing_types_count_compared_to_pyqt += 1
 
             # missing in PySide
             if not is_present_in_pyside:
@@ -321,7 +336,7 @@ if __name__ == "__main__":
         plt.xticks(rotation=45)
         plt.ylabel("Types Count")
         figure = plt.gcf()
-        figure.set_size_inches(32, 18) # set to full_screen
+        figure.set_size_inches(32, 18)  # set to full_screen
         plt.savefig("missing_bindings_comparison_plot.png", bbox_inches='tight')
         print(f"Plot saved in {Path.cwd() / 'missing_bindings_comparison_plot.png'}\n")
 
index e4e38167594660a2040be5a9677d0108d10a51d8..86c513859c5d2c2cb6a1a0791c9bdc7cd90d8e0e 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import logging
 import os
index 098c58b1ff286699db42909bf45b239f3d526e2f..90bc50eb3e12eda54db815b4d8bb571e4e0eea21 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 regenerate_example_resources.py
index 2e0881c0725a54f844e0b0835f15935fc5a2ed53..47f98a6a1ac1a1a73089d7b13b145b8a29188dc2 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 """
 regenerate_example_ui.py
diff --git a/tools/release_notes/main.py b/tools/release_notes/main.py
new file mode 100644 (file)
index 0000000..77ce474
--- /dev/null
@@ -0,0 +1,197 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+"""
+This tool reads all the changelogs in doc/changelogs and generates .rst files for each of the
+changelogs. This .rst files are then used to generate the contents of the 'Release Notes' section
+in the navigation pane of the Qt for Python documentation.
+"""
+
+import re
+import logging
+import shutil
+from pathlib import Path
+from argparse import ArgumentParser, RawTextHelpFormatter
+
+SECTION_NAMES = ["PySide6", "Shiboken6", "PySide2", "Shiboken2"]
+DIR = Path(__file__).parent
+DEFAULT_OUTPUT_DIR = Path(f"{DIR}/../../sources/pyside6/doc/release_notes").resolve()
+CHANGELOG_DIR = Path(f"{DIR}/../../doc/changelogs").resolve()
+
+BASE_CONTENT = """\
+.. _release_notes:
+
+Release Notes
+=============
+
+This section contains the release notes for different versions of Qt for Python.
+
+.. toctree::
+    :maxdepth: 1
+
+    pyside6_release_notes.md
+    shiboken6_release_notes.md
+    pyside2_release_notes.md
+    shiboken2_release_notes.md
+"""
+
+
+class Changelog:
+    def __init__(self, file_path: Path):
+        self.file_path = file_path
+        self.version = file_path.name.split("-")[-1]
+        self.sections = {section: [] for section in SECTION_NAMES}
+        # for matching lines like *    PySide6    * to identify the section
+        self.section_pattern = re.compile(r"\* +(\w+) +\*")
+        # for line that start with ' -' which lists the changes
+        self.line_pattern = re.compile(r"^ -")
+        # for line that contains a bug report like PYSIDE-<bug_number>
+        self.bug_number_pattern = re.compile(r"\[PYSIDE-\d+\]")
+
+    def add_line(self, section, line):
+        self.sections[section].append(line)
+
+    def parsed_sections(self):
+        return self.sections
+
+    def parse(self):
+        current_section = None
+        buffer = []
+
+        with open(self.file_path, 'r', encoding='utf-8') as file:
+            # convert the lines to an iterator for skip the '***' lines
+            lines = iter(file.readlines())
+
+        for line in lines:
+            # skip lines with all characters as '*'
+            if line.strip() == '*' * len(line.strip()):
+                continue
+
+            match = self.section_pattern.match(line)
+            if match:
+                # if buffer has content, add it to the current section
+                if buffer:
+                    self.add_line(current_section, ' '.join(buffer).strip())
+                    buffer = []
+                current_section = match.group(1)
+                # skip the next line which contains '***'
+                try:
+                    next(lines)
+                except StopIteration:
+                    break
+                continue
+
+            if current_section:
+                if self.line_pattern.match(line) and buffer:
+                    self.add_line(current_section, ' '.join(buffer).strip())
+                    buffer = []
+
+                # If the line contains a reference to a bug report like [PYSIDE-<bug_number>]
+                # then insert a link to the reference that conforms with Sphinx syntax
+                bug_number = self.bug_number_pattern.search(line)
+                if bug_number:
+                    bug_number = bug_number.group()
+                    # remove the square brackets
+                    actual_bug_number = bug_number[1:-1]
+                    bug_number_replacement = (
+                        f"[{actual_bug_number}]"
+                        f"(https://bugreports.qt.io/browse/{actual_bug_number})"
+                    )
+                    line = re.sub(re.escape(bug_number), bug_number_replacement, line)
+
+                # Add the line to the buffer
+                buffer.append(line.strip())
+
+        # Add any remaining content in the buffer to the current section
+        if buffer:
+            self.add_line(current_section, ' '.join(buffer).strip())
+
+
+def parse_changelogs() -> str:
+    '''
+    Parse the changelogs in the CHANGELOG_DIR and return a list of parsed changelogs.
+    '''
+    changelogs = []
+    logging.info(f"[RELEASE_DOC] Processing changelogs in {CHANGELOG_DIR}")
+    for file_path in CHANGELOG_DIR.iterdir():
+        # exclude changes-1.2.3
+        if "changes-1.2.3" in file_path.name:
+            continue
+        logging.info(f"[RELEASE_DOC] Processing file {file_path.name}")
+        changelog = Changelog(file_path)
+        changelog.parse()
+        changelogs.append(changelog)
+    return changelogs
+
+
+def write_md_file(section: str, changelogs: list[Changelog], output_dir: Path):
+    '''
+    For each section create a .md file with the following content:
+
+    Section Name
+    ============
+
+    Version
+    -------
+
+    - Change 1
+    - Change 2
+    ....
+    '''
+    file_path = output_dir / f"{section.lower()}_release_notes.md"
+    with open(file_path, 'w', encoding='utf-8') as file:
+        file.write(f"# {section}\n")
+        for changelog in changelogs:
+            section_contents = changelog.parsed_sections()[section]
+            if section_contents:
+                file.write(f"## {changelog.version}\n\n")
+                for lines in section_contents:
+                    # separate each line with a newline
+                    file.write(f"{lines}\n")
+                file.write("\n")
+
+
+def generate_index_file(output_dir: Path):
+    """Generate the index RST file."""
+    index_path = output_dir / "index.rst"
+    index_path.write_text(BASE_CONTENT, encoding='utf-8')
+
+
+def main():
+    parser = ArgumentParser(description="Generate release notes from changelog",
+                            formatter_class=RawTextHelpFormatter)
+    parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
+                        dest="loglevel", const=logging.INFO)
+    parser.add_argument("--target", "-t", help="Directory to output the generated files",
+                        type=Path, default=DEFAULT_OUTPUT_DIR)
+    args = parser.parse_args()
+
+    logging.basicConfig(level=args.loglevel)
+
+    output_dir = args.target.resolve()
+
+    # create the output directory if it does not exist
+    # otherwise remove its contents
+    if output_dir.is_dir():
+        shutil.rmtree(output_dir, ignore_errors=True)
+        logging.info(f"[RELEASE_DOC] Removed existing {output_dir}")
+
+    logging.info(f"[RELEASE_DOC] Creating {output_dir}")
+    output_dir.mkdir(exist_ok=True)
+
+    logging.info("[RELEASE_DOC] Generating index.md file")
+    generate_index_file(output_dir)
+
+    logging.info("[RELEASE_DOC] Parsing changelogs")
+    changelogs = parse_changelogs()
+
+    # sort changelogs by version number in descending order
+    changelogs.sort(key=lambda x: x.version, reverse=True)
+
+    for section in SECTION_NAMES:
+        logging.info(f"[RELEASE_DOC] Generating {section.lower()}_release_notes.md file")
+        write_md_file(section, changelogs, output_dir)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/scanqtclasses.py b/tools/scanqtclasses.py
new file mode 100644 (file)
index 0000000..a74fe95
--- /dev/null
@@ -0,0 +1,123 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+from pathlib import Path
+import os
+import re
+import subprocess
+import sys
+
+"""Scan the Qt C++ headers per module for classes that should be present
+   in the matching type system and print the missing classes."""
+
+
+VALUE_TYPE = re.compile(r'^\s*<value-type name="([^"]+)"')
+
+
+OBJECT_TYPE = re.compile(r'^\s*<object-type name="([^"]+)"')
+
+
+def query_qtpaths(keyword):
+    """Query qtpaths for a keyword."""
+    query_cmd = ["qtpaths", "-query", keyword]
+    output = subprocess.check_output(query_cmd, stderr=subprocess.STDOUT,
+                                     universal_newlines=True)
+    return output.strip()
+
+
+def is_class_exluded(name):
+    """Check for excluded classes that do not make sense in a typesystem."""
+    if len(name) < 2:
+        return True
+    if "Iterator" in name or "iterator" in name:
+        return True
+    if name.startswith("If") or name.startswith("Is") or name.startswith("When"):
+        return True
+    if name[:1].islower():
+        return True
+    if name.startswith("QOpenGLFunctions") and name.endswith("Backend"):
+        return True
+    return False
+
+
+def class_from_header_line(line):
+    """Extract a class name from a C++ header line."""
+    def _is_macro(token):
+        return "EXPORT" in token or "API" in token
+
+    def _fix_class_name(name):
+        pos = name.find('<')  # Some template specialization "class Name<TemplateParam>"
+        if pos > 0:
+            name = name[:pos]
+        if name.endswith(':'):
+            name = name[:-1]
+        return name
+
+    if line.startswith('//') or line.endswith(';'):  # comment/forward decl
+        return None
+    line = line.strip()
+    if not line.startswith("class ") and not line.startswith("struct "):
+        return None
+    tokens = line.split()
+    pos = 1
+    while pos < len(tokens) and _is_macro(tokens[pos]):
+        pos += 1
+    return _fix_class_name(tokens[pos]) if pos < len(tokens) else None
+
+
+def classes_from_header(header):
+    """Extract classes from C++ header file."""
+    result = []
+    for line in header.read_text("utf-8").splitlines():
+        name = class_from_header_line(line)
+        if name and not is_class_exluded(name):
+            result.append(name)
+    return sorted(result)
+
+
+def classes_from_typesystem(typesystem):
+    """Extract classes from typesystem XML file."""
+    result = []
+    for line in typesystem.read_text("utf-8").splitlines():
+        match = VALUE_TYPE.search(line) or OBJECT_TYPE.search(line)
+        if match:
+            result.append(match.group(1))
+    return sorted(result)
+
+
+def check_classes(qt_module_inc_dir, pyside_dir):
+    """Check classes of a module."""
+    module_name = qt_module_inc_dir.name
+    sys.stderr.write(f"Checking {module_name} ")
+    cpp_classes = []
+    typesystem_classes = []
+    for header in qt_module_inc_dir.glob("q*.h"):
+        if not header.name.endswith("_p.h"):
+            cpp_classes.extend(classes_from_header(header))
+    for typesystem in pyside_dir.glob("*.xml"):
+        typesystem_classes.extend(classes_from_typesystem(typesystem))
+
+    cpp_count = len(cpp_classes)
+    typesystem_count = len(typesystem_classes)
+    sys.stderr.write(f"found {cpp_count} C++ / {typesystem_count} typesystem classes")
+    if cpp_count <= typesystem_count:
+        sys.stderr.write(" ok\n")
+    else:
+        sys.stderr.write(f", {cpp_count-typesystem_count} missing\n")
+        for cpp_class in cpp_classes:
+            if cpp_class not in typesystem_classes:
+                wrapper_name = cpp_class.lower() + "_wrapper.cpp"
+                print(f"{module_name}:{cpp_class}:{wrapper_name}")
+
+
+if __name__ == '__main__':
+    qt_version = query_qtpaths("QT_VERSION")
+    qt_inc_dir = Path(query_qtpaths("QT_INSTALL_HEADERS"))
+    print(f"Qt {qt_version} at {os.fspath(qt_inc_dir.parent)}", file=sys.stderr)
+
+    dir = Path(__file__).parents[1].resolve()
+    for module_dir in (dir / "sources" / "pyside6" / "PySide6").glob("Qt*"):
+        qt_module_inc_dir = qt_inc_dir / module_dir.name
+        if qt_module_inc_dir.is_dir():
+            check_classes(qt_module_inc_dir, module_dir)
index d45bf277f7f8474aa97d4bd4effe72887fdd43b0..e5880a09b06da84b63501ec656ca4b3411bfa6fd 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import re
 
@@ -55,12 +56,12 @@ switch_branch = 0
 def snippet_translate(x):
     global switch_var, switch_branch
 
-    ## Cases which are not C++
-    ## TODO: Maybe expand this with lines that doesn't need to be translated
+    # # Cases which are not C++
+    # # TODO: Maybe expand this with lines that doesn't need to be translated
     if x.strip().startswith("content-type: text/html"):
         return x
 
-    ## General Rules
+    # # General Rules
 
     # Remove ';' at the end of the lines
     has_semicolon = x.endswith(";")
@@ -179,7 +180,7 @@ def snippet_translate(x):
     if "nullptr" in x:
         x = x.replace("nullptr", "None")
 
-    ## Special Cases Rules
+    # # Special Cases Rules
     xs = x.strip()
     # Special case for 'main'
     if xs.startswith("int main("):
@@ -297,8 +298,7 @@ def snippet_translate(x):
         # with only alpha numeric content
         if VAR4_PATTERN.search(xs) and not xs.endswith(")"):
             v = x.rstrip()
-            if (not v.endswith(" True") and not v.endswith(" False")
-                and not v.endswith(" None")):
+            if (not v.endswith(" True") and not v.endswith(" False") and not v.endswith(" None")):
                 x = f"{v}()"
         return dstrip(x)
 
index 34e969a62af9a322fad0c8c0e68fe312b83177a5..10bc9d00e3fc9059292288f90577f14a1b7669b6 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import re
 import sys
@@ -86,7 +87,7 @@ def handle_inc_dec(x, operator):
     clean_x = x.strip()
     if clean_x.startswith(operator) or clean_x.endswith(operator):
         x = x.replace(operator, "")
-        x = f"{x} = {clean_x.replace(operator, '')} {operator[0]} 1"
+        x = f"{x} {operator[0]}= 1"
     return x
 
 
index 01ea06c5e66dfc750167b685dd426bb58e4622b3..9e63dd8d08de2a6acf03213fd297de532b04f9d9 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import logging
 import os
@@ -9,7 +10,6 @@ from argparse import ArgumentParser, Namespace, RawDescriptionHelpFormatter
 from enum import Enum
 from pathlib import Path
 from textwrap import dedent
-from typing import Dict, List
 
 from override import python_example_snippet_mapping
 from converter import snippet_translate
@@ -184,7 +184,7 @@ def is_valid_file(x):
     return True
 
 
-def get_snippet_ids(line: str, pattern: re.Pattern) -> List[str]:
+def get_snippet_ids(line: str, pattern: re.Pattern) -> list[str]:
     # Extract the snippet ids for a line '//! [1] //! [2]'
     result = []
     for m in pattern.finditer(line):
@@ -192,7 +192,7 @@ def get_snippet_ids(line: str, pattern: re.Pattern) -> List[str]:
     return result
 
 
-def overriden_snippet_lines(lines: List[str], start_id: str) -> List[str]:
+def overriden_snippet_lines(lines: list[str], start_id: str) -> list[str]:
     """Wrap an overridden snippet with marker and id lines."""
     id_string = f"//! [{start_id}]"
     result = [OVERRIDDEN_SNIPPET, id_string]
@@ -201,7 +201,7 @@ def overriden_snippet_lines(lines: List[str], start_id: str) -> List[str]:
     return result
 
 
-def get_snippet_override(start_id: str, rel_path: str) -> List[str]:
+def get_snippet_override(start_id: str, rel_path: Path) -> list[str]:
     """Check if the snippet is overridden by a local file under
        sources/pyside6/doc/snippets."""
     file_start_id = start_id.replace(' ', '_')
@@ -213,14 +213,14 @@ def get_snippet_override(start_id: str, rel_path: str) -> List[str]:
     return overriden_snippet_lines(lines, start_id)
 
 
-def _get_snippets(lines: List[str],
+def _get_snippets(lines: list[str],
                   comment: str,
-                  pattern: re.Pattern) -> Dict[str, List[str]]:
+                  pattern: re.Pattern) -> dict[str, list[str]]:
     """Helper to extract (potentially overlapping) snippets from a C++ file
        indicated by pattern ("//! [1]") and return them as a dict by <id>."""
-    snippets: Dict[str, List[str]] = {}
-    snippet: List[str]
-    done_snippets : List[str] = []
+    snippets: dict[str, list[str]] = {}
+    snippet: list[str]
+    done_snippets: list[str] = []
 
     i = 0
     while i < len(lines):
@@ -243,14 +243,14 @@ def _get_snippets(lines: List[str],
             # Find the end of the snippet
             j = i
             while j < len(lines):
-                l = lines[j]
+                line = lines[j]
                 j += 1
 
                 # Add the line to the snippet
-                snippet.append(l)
+                snippet.append(line)
 
                 # Check if the snippet is complete
-                if start_id in get_snippet_ids(l, pattern):
+                if start_id in get_snippet_ids(line, pattern):
                     # End of snippet
                     snippet[len(snippet) - 1] = id_line
                     snippets[start_id] = snippet
@@ -259,7 +259,7 @@ def _get_snippets(lines: List[str],
     return snippets
 
 
-def get_python_example_snippet_override(start_id: str, rel_path: str) -> List[str]:
+def get_python_example_snippet_override(start_id: str, rel_path: Path) -> list[str]:
     """Check if the snippet is overridden by a python example snippet."""
     key = (os.fspath(rel_path), start_id)
     value = python_example_snippet_mapping().get(key)
@@ -275,7 +275,7 @@ def get_python_example_snippet_override(start_id: str, rel_path: str) -> List[st
     return overriden_snippet_lines(lines, start_id)
 
 
-def get_snippets(lines: List[str], rel_path: str) -> List[List[str]]:
+def get_snippets(lines: list[str], rel_path: Path) -> list[list[str]]:
     """Extract (potentially overlapping) snippets from a C++ file indicated
        by '//! [1]'."""
     result = _get_snippets(lines, '//', CPP_SNIPPET_PATTERN)
@@ -288,7 +288,7 @@ def get_snippets(lines: List[str], rel_path: str) -> List[List[str]]:
         if snippet:
             result[snippet_id] = snippet
 
-    return result.values()
+    return list(result.values())
 
 
 def get_license_from_file(lines):
index df4c7557c99c9c23548c58164f8b7fe17ae90ca5..b8e9ef0c7eead86c6e5a8e3bc7256575271fa7db 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 
 module_classes = {
@@ -528,6 +529,8 @@ module_classes = {
         "QAbstractOpenGLFunctions",
         "QAbstractTextDocumentLayout",
         "QAccessible",
+        "QAccessibleAnnouncementEvent",
+        "QAccessibleAttributesInterface",
         "QAccessibleEditableTextInterface",
         "QAccessibleEvent",
         "QAccessibleInterface",
index e7623d8a55c22287069e18dd8cdcf565806e4e2f..eab3f178ecbb78927da6d60210c274b7a4c7f190 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from pathlib import Path
 
@@ -79,33 +80,33 @@ def python_example_snippet_mapping():
                        / "stringlistmodel.py")
         for i in range(10):
             snippet_id = str(i)
-            result[(qt_path, snippet_id)] = pyside_path,  snippet_id
+            result[(qt_path, snippet_id)] = pyside_path, snippet_id
 
         qt_path = "qtbase/src/widgets/doc/snippets/qlistview-dnd/model.cpp"
         pyside_path = (TUTORIAL_EXAMPLES_PATH / "modelviewprogramming"
                        / "qlistview-dnd.py")
         for i in range(11):
             snippet_id = str(i)
-            result[(qt_path, snippet_id)] = pyside_path,  snippet_id
+            result[(qt_path, snippet_id)] = pyside_path, snippet_id
 
         qt_path = "qtconnectivity/examples/bluetooth/heartrate_game/devicefinder.cpp"
         pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_game" / "devicefinder.py"
         for i in range(5):
             snippet_id = f"devicediscovery-{i}"
-            result[(qt_path, snippet_id)] = pyside_path,  snippet_id
+            result[(qt_path, snippet_id)] = pyside_path, snippet_id
 
         qt_path = "qtconnectivity/examples/bluetooth/heartrate_game/devicehandler.cpp"
         pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_game" / "devicehandler.py"
         for snippet_id in ["Connect-Signals-1", "Connect-Signals-2",
                            "Filter HeartRate service 2", "Find HRM characteristic",
                            "Reading value"]:
-            result[(qt_path, snippet_id)] = pyside_path,  snippet_id
+            result[(qt_path, snippet_id)] = pyside_path, snippet_id
 
         qt_path = "qtconnectivity/examples/bluetooth/heartrate_server/main.cpp"
         pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_server" / "heartrate_server.py"
         for snippet_id in ["Advertising Data", "Start Advertising", "Service Data",
                            "Provide Heartbeat"]:
-            result[(qt_path, snippet_id)] = pyside_path,  snippet_id
+            result[(qt_path, snippet_id)] = pyside_path, snippet_id
 
         _python_example_snippet_mapping = result
 
index 234d1b66920e0a0629b4c64a804189968b5588a8..688938628f0bc0252079832f2fc141046c450759 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import re
 
index 084cc8a6decc48a5db5382a21554c18ce43e1f3f..beab3ad09292e5f9c7dbe69b84e0a955b8cd158c 100644 (file)
@@ -1,11 +1,12 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from converter import snippet_translate as st
 
 
 def multi_st(lines):
-    result = [st(l) for l in lines.split("\n")]
+    result = [st(line) for line in lines.split("\n")]
     return "\n".join(result)
 
 
@@ -35,8 +36,8 @@ def test_curly_braces():
 
 
 def test_inc_dec():
-    assert st("++i;") == "i = i + 1"
-    assert st("i--;") == "i = i - 1"
+    assert st("++i;") == "i += 1"
+    assert st("i--;") == "i -= 1"
 
 
 def test_and_or():
@@ -101,7 +102,7 @@ def test_cast():
         == "elapsed = (elapsed + QTimer(sender()).interval()) % 1000"
     )
     assert (
-         st("a = qobject_cast<type*>(data) * 9 + static_cast<int>(42)")
+         st("a = qobject_cast<type*>(data) * 9 + static_cast<int>(42)")  # noqa: E126
          == "a = type(data) * 9 + int(42)"
     )
 
@@ -120,15 +121,15 @@ def test_double_colon():
 def test_connects():
     assert (
         st("connect(button, &QPushButton::clicked, this, &MyClass::slotClicked);")
-        ==  "button.clicked.connect(self.slotClicked)"
+        == "button.clicked.connect(self.slotClicked)"
     )
     assert (
         st("connect(m_ui->button, &QPushButton::clicked, this, &MyClass::slotClicked);")
-        ==  "m_ui.button.clicked.connect(self.slotClicked)"
+        == "m_ui.button.clicked.connect(self.slotClicked)"
     )
     assert (
         st("connect(button.get(), &QPushButton::clicked, this, &MyClass::slotClicked);")
-        ==  "button.clicked.connect(self.slotClicked)"
+        == "button.clicked.connect(self.slotClicked)"
     )
 
 
@@ -203,7 +204,7 @@ def test_for():
     assert st("for (int i = 0; i <= 10; i++)") == "for i in range(0, 11):"
     assert st("for (int i = 10; i >= 0; i--)") == "for i in range(-1, 10, -1):"
 
-    ## if contains "begin()" and "end()", do a 'for it in var'
+    # # if contains "begin()" and "end()", do a 'for it in var'
     assert (
         st(
             "for (QHash<int, QString>::const_iterator it = hash.cbegin(),"
@@ -432,7 +433,8 @@ def test_special_cases():
     # assert st("QDebug operator<<(QDebug dbg, const Message &message)") == "def __str__(self):"
 
     # TODO: Maybe play with the format?
-    # assert st('m_o.append(tr("version: %1.%2").arg(a).arg(b))') == 'm_o.append(tr("version: {1}.{2}".format(a, b)'
+    # assert st('m_o.append(tr("version: %1.%2").arg(a).arg(b))')
+    #           == 'm_o.append(tr("version: {1}.{2}".format(a, b)'
 
 
 def test_lambdas():
index 84897d81573690051e0b6ea3702dd918f0dd36e1..e914077e7613e97712647340b15cfc1dea268afb 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 from main import _get_snippets, get_snippet_ids, CPP_SNIPPET_PATTERN
 
index 208536963c7c976aeab83b2b115fc4ca98814c5b..eccddae97d8bad956102c6e6b911948b566a23ba 100644 (file)
@@ -1,5 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
 
 import os
 import re
@@ -9,7 +10,6 @@ import tempfile
 from argparse import ArgumentParser, RawTextHelpFormatter
 from pathlib import Path
 from textwrap import dedent
-from typing import Optional, Tuple
 
 VERSION = 6
 
@@ -20,7 +20,7 @@ DESC = """Runs uic on a set of UI files and displays the resulting widgets."""
 TEMP_DIR = Path(tempfile.gettempdir())
 
 
-def get_class_name(file: Path) -> Tuple[Optional[str], Optional[str]]:
+def get_class_name(file: Path) -> tuple[str | None, str | None]:
     """Return class name and widget name of UI file."""
     pattern = re.compile(r'^\s*<widget class="(\w+)" name="(\w+)"\s*>.*$')
     for line in Path(file).read_text().splitlines():
@@ -78,7 +78,7 @@ if __name__ == '__main__':
     failed = 0
     count = len(options.files)
     for i, file in enumerate(options.files):
-        print(f'{i+1}/{count} {file}')
+        print(f'{i + 1}/{count} {file}')
         if not test_file(file, options.uic):
             failed += 1
     if failed != 0:
index 2e30e9ae90a7881849f2ae101c59ef8f3bb7116b..3e289fd7980c1a4bf50a34447ffcaeed5f15566b 100644 (file)
@@ -3,17 +3,17 @@ requires = ["setuptools", "wheel"]
 build-backend = "setuptools.build_meta"
 
 [project]
-name = PROJECT_NAME
+name = "PROJECT_NAME"
 authors = [
     {name = "Qt for Python Team", email = "pyside@qt-project.org"},
 ]
-description = PROJECT_DESCRIPTION
-readme = PROJECT_README
+description = "PROJECT_DESCRIPTION"
+readme = "PROJECT_README"
 dynamic = ["version"]
-requires-python = ">=3.9, <3.13"
+requires-python = ">=3.9, <3.14"
 keywords = ["Qt"]
 license = {text = "LGPL"}
-dependencies = PROJECT_DEPENDENCIES
+dependencies = "PROJECT_DEPENDENCIES"
 classifiers = [
     "Development Status :: 5 - Production/Stable",
     "Environment :: Console",
@@ -35,6 +35,7 @@ classifiers = [
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
     "Topic :: Database",
     "Topic :: Software Development",
     "Topic :: Software Development :: Code Generators",
@@ -50,11 +51,11 @@ Repository = "https://code.qt.io/cgit/pyside/pyside-setup.git/"
 Changelog = "https://code.qt.io/cgit/pyside/pyside-setup.git/tree/doc/changelogs"
 Tracker = "https://bugreports.qt.io/projects/PYSIDE"
 
-PROJECT_SCRIPTS
+# PROJECT_SCRIPTS
 
 [tool.distutils.bdist_wheel]
 py_limited_api = "cp39"
-plat_name = PROJECT_TAG
+plat_name = "PROJECT_TAG"
 
 [tool.setuptools.dynamic]
-version = {attr = PROJECT_VERSION}
+version = {attr = "PROJECT_VERSION"}